【问题标题】:Coalesce two SQL queries合并两个 SQL 查询
【发布时间】:2016-05-26 09:02:02
【问题描述】:

我需要一个模仿 if-then-else 语句形式的 SQL 查询:

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 |ten | ... )。我想选择那些与@instant 重叠的行。如果没有找到重叠行,则应返回@instant 之前最近的行。

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

【问题讨论】:

    标签: sql postgresql select coalesce isnull


    【解决方案1】:

    这对我有用。我合并了这两个查询,然后对记录进行了排名。它将在 table2 之前从 table1 中获取记录。

    with prev_tables as (
            select name, id, address, 1 as ranking
            from table1
            union 
            select name, id, address, 2
            from table2
        ),
        prev_dedupe as (
            select p.*, rank() over (partition by id order by ranking) as prev_table_rk
            from prev_tables p
        )
    select * from prev_dedupe where prev_table_rk = 1
    

    【讨论】:

      【解决方案2】:

      首先,在这种情况下,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;
      

      【讨论】:

      • @Matthias 。 . . UNION 会产生删除重复值的开销。我不认为这两个子查询可以有重复的值,所以没有理由产生这样的开销。
      • 是的,查询不应该产生重复 - 因此我的问题。所以这是一点点优化。谢谢。
      • 我会接受你的回答。第一个解决方案对我有用。 WHERE (NOT EXISTS (SELECT 1 FROM t1)) 中缺少括号 - 请添加。
      猜你喜欢
      • 1970-01-01
      • 2020-12-23
      • 2017-05-18
      • 2016-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-28
      相关资源
      最近更新 更多