(My)SQL:如果子查询不是空集,则返回子查询

Posted

技术标签:

【中文标题】(My)SQL:如果子查询不是空集,则返回子查询【英文标题】:(My)SQL: If subquery is not an empty set, return subquery 【发布时间】:2017-06-14 13:01:34 【问题描述】:

这个问题是专门针对 mysql 的,但我试图以适用标准 SQL 的方式提问。

上下文:我正在尝试通过以下方式确定结束日期:如果在输入的开始日期之后存在另一个开始日期,则使用现有开始日期作为结束日期;否则,结束日期应在输入的开始日期后 30 天。

我尝试过的解决方案类似于以下:

SELECT
  IF(
    EXISTS(  
      SELECT
        DISTINCT start_date
      FROM table
      WHERE ? < start_date AND
            identifier = ?
      ORDER BY start_date
      LIMIT 1
    ), (
    SELECT
      DISTINCT start_date
    FROM table
    WHERE ? < start_date AND
          identifier = ?
    ORDER BY start_date
    LIMIT 1),
    DATE_ADD(?, INTERVAL 30 DAY)
  ) AS end_date

我的解决方案有效,但我希望有一个更优雅、不重复的解决方案。

一般的解决方案是——如果存在子查询——从子查询返回值;否则,可以退回其他东西。

【问题讨论】:

【参考方案1】:

代替子查询做一个左连接(到表本身)

SELECT
COALESCE(t2.start_date, t1.start_date)
FROM table t1
LEFT JOIN table t2 ON t1.identifier = t2.identifier AND t1.start_date > t2.start_date

左连接条目要么存在,要么不存在,这意味着它不为空或不为空。 COALESCE() 函数返回其第一个不为空的参数。

【讨论】:

这个解决方案是生产矿山不可或缺的;虽然JOIN 声明并不完全符合我的目标,但我不熟悉COALESCE 的工作方式。您加深了我对该功能的理解,使我能够开发我标记为已接受的解决方案(一旦我可以这样做)。谢谢!【参考方案2】:

针对您自己的回答,我建议使用:

SELECT
  COALESCE((
    SELECT MIN(start_date)
    FROM TABLE
    WHERE start_date > ? 
    AND   identifier = ?), (
    SELECT
      DATE_ADD(?, INTERVAL 30 DAY)
    )) AS end_date 

似乎更容易理解恕我直言。尽管它看起来不同,但它在幕后做的事情几乎相同。

【讨论】:

更有效,没有ORDER BY 语句,而且你这么清楚地指出我完全忘记了MIN() 是一个函数!成功。标记为已接受(主要是因为我不想标记我自己问题的答案,但也因为你的解决方案更好)。【参考方案3】:

根据fancyPants' 的解决方案,我使用了COALESCE,但方式完全不同(因此我不能完全将回复标记为已接受)。

SELECT
  COALESCE((
    SELECT
      DISTINCT start_date
    FROM TABLE
    WHERE ? < start_date AND
          identifier = ?
    ORDER BY start_Date
    LIMIT 1), (
    SELECT
      DATE_ADD(?, INTERVAL 30 DAY)
    )) AS end_date

上述查询将完全按照预期进行。您的子查询是COALESCE 语句的第一个参数,备用查询是第二个。

【讨论】:

以上是关于(My)SQL:如果子查询不是空集,则返回子查询的主要内容,如果未能解决你的问题,请参考以下文章

如果子查询没有返回结果,为啥“= ALL(子查询)”评估为真?

MySQL的SQL语句 - 数据操作语句(13)- 子查询

MySQL的SQL语句 - 数据操作语句(13)- 子查询

UDF 与子查询性能问题

子查询语法详解

关于高级查询的知识