合并两个SQL查询

时间:2016-02-15 12:37:08

标签: sql postgresql select coalesce isnull

我需要一个SQL查询,它模仿表格的if-then-else语句:

if (query1 != null)
  return query1
else
  return query2

由于COALESCE不能使用结果集,我创建了一个联合查询来完成这项工作:

SELECT * FROM obs WHERE cond1  --query1
UNION
SELECT * FROM obs WHERE (NOT EXISTS(query1)) AND cond2

在SQL中:

  ( SELECT * FROM obs WHERE src = @id AND tstart <= @instant AND tend >= @instant )
  UNION
  ( SELECT * FROM obs WHERE (NOT EXISTS (SELECT 1 FROM obs WHERE src = @id AND tstart <= @instant AND tend >= @instant )) AND src = @id AND tstart <= @instant ORDER BY tend DESC LIMIT 1);

表obs有字段(src | tstart | tend | ...)。我想选择那些与@instant重叠的行。如果未找到重叠的行,则应返回@instant之前的最近一行。

SQL UNION语句有效,但它非常笨拙,我正在寻找一个更短更清晰的语句。 COALESCE(query1,query2)精神的东西会很好。我的数据库是Postgresql。

1 个答案:

答案 0 :(得分:3)

首先,在这种情况下,union all可能比union更合适。

其次,您可以使用with来简化查询:

WITH t1 as (
      SELECT *
      FROM obs
      WHERE src = @id AND tstart <= @instant AND tend >= @instant
     )
SELECT t1.*
FROM t1
UNION ALL
(SELECT *
 FROM obs
 WHERE NOT EXISTS (SELECT 1 FROM t1) AND
       src = @id AND tstart <= @instant
 ORDER BY tend DESC
 LIMIT 1
);

但是,如果您正在寻找单行,这更简单:

 SELECT *
 FROM obs
 WHERE src = @id
 ORDER BY (CASE WHEN  tstart <= @instant AND tend >= @instant THEN 1
                ELSE 2
           END),
          tend DESC
 LIMIT 1;

而且,如果不是单行,则也可以使用窗口函数:

SELECT o.*
FROM (SELECT o.*,
             DENSE_RANK() OVER (PARTITION BY src
                                ORDER BY (CASE WHEN tstart <= @instant AND tend >= @instant THEN 1
                                               ELSE 2
                                          END),
                                         (CASE WHEN tstart <= @instant AND tend >= @instant THEN NULL
                                               ELSE tend
                                          END) DESC
                               ) as seqnum
      FROM obs o
      WHERE src = @id
     ) o
WHERE seqnum = 1;