当子查询不返回任何行时删除 WHERE 条件

Posted

技术标签:

【中文标题】当子查询不返回任何行时删除 WHERE 条件【英文标题】:Remove WHERE condition when subquery does not return any row 【发布时间】:2011-08-12 17:03:27 【问题描述】:

我正在编写一个复杂的 mysql 查询,但不知道如何完成它。

这是给我带来麻烦的部分(这只是我查询的一部分):

SELECT * FROM table AS t1
WHERE date < (
    SELECT date FROM table AS t2 
    WHERE phase="B" AND t2.target = t1.target
)

基本上,我有项目,每个项目都有一个日期、一个阶段(A、B、C)和一个目标。对于一个目标,有几个 A 类型的项目,然后是 B 类型的 单个可选 项目,然后是 C 类型的项目。

对于每个目标,我想选择符合以下条件的所有行:

    如果有一个阶段为“B”的项目(我们称他为 itemX),我想返回所有日期低于 itemX 日期的项目 如果没有“B”阶段的项目,我想返回所有行

日期参数非常重要。在大多数情况下,这 3 个阶段是不同的,不能重叠,但在某些情况下会发生这种情况。

这里的问题是,我的子查询在案例 1 中没有返回任何行,在案例 2 中没有返回单个单元格。

如果是情况 1,则整个条件 WHERE date &lt; (...) 是不相关的,不应在查询中应用。

我尝试了IFNULLEXISTS 的几种可能性,但我认为我做错了,因为我不断收到语法错误。

【问题讨论】:

【参考方案1】:
SELECT  m.*
FROM    (
        SELECT  target, MAX(date) AS maxdate
        FROM    mytable
        ) md
JOIN    mytable m
ON      m.target = md.target
        AND m.date <
        COALESCE
        (
        (
        SELECT  date
        FROM    mytable mb
        WHERE   mb.target = md.target
                AND mb.phase = 'B'
        ORDER BY
                mb.target, pmb.phase, mb.date
        LIMIT 1
        ),
        maxdate + INTERVAL 1 SECOND
        )

创建两个索引:

mytable (target, date)
mytable (target, phase, date)

让这个工作快速。

【讨论】:

【参考方案2】:

也许

SELECT *
FROM table AS t1
LEFT JOIN table AS t2 ON t2.target = t1.target AND (t1.date < t2.date)
WHERE (phase = 'B')

我假设您查询中的table 实际上是两个表,而您没有进行自联接?如果是这样,那么您必须指定您指的是哪个表的phase

【讨论】:

【参考方案3】:

你可以试试

SELECT * FROM table AS t1
left join 
table as t2 
on t1.Target = t2.Target
and t2.phase="B"
where t2.target is null OR 
OR t1.date < t2.Date

【讨论】:

我不明白t2.target is null 部分。你能解释一下吗? 通过左连接,您将包括连接左侧的所有行,并且仅匹配连接右侧的行。假设左侧表 t1 和右侧表 t2,在不满足连接条件的情况下,t2 中任何列的值都将为空。由于在这种情况下的目标是在我们的连接条件不满足时省略 where 子句(目标匹配并且 t2 阶段的值为“B”),我们首先检查连接条件是否失败,如果是,我们返回一行。 如果连接条件成功,如果是t1日期值小于t2日期值的情况,我们返回一行。由于 t2.Target 是我们连接条件的一部分,因此只有在不匹配的情况下才会为 null。【参考方案4】:

您发布的代码称为“每个条件反模式一个子查询”。使用 CASE-WHEN-THEN。

【讨论】:

【参考方案5】:
SELECT t1.*
  FROM table t1
  LEFT
  JOIN ( SELECT t.target
              , MIN(t.date) AS b_date
           FROM table t
          WHERE t.phase = 'B'
          GROUP BY t.target
       ) t2
    ON t1.target = t2.target AND t1.date < t2.b_date

如果可以保证给定目标最多只有一行“phase”='B',则可以不使用 MIN 和 GROUP BY,如下所示:

SELECT t1.*
  FROM table t1
  LEFT
  JOIN ( SELECT t.target
              , t.date AS b_date
           FROM table t
          WHERE t.phase = 'B'
       ) t2
    ON t1.target = t2.target AND t1.date < t2.b_date

【讨论】:

以上是关于当子查询不返回任何行时删除 WHERE 条件的主要内容,如果未能解决你的问题,请参考以下文章

子查询返回超过 1 个值。当子查询跟随 = 或子查询用作表达式时,这是不允许的

子查询返回的值不止一个.当子查询跟随在 =,!=,<,<=,>,>= 之后,或子查询用作

SQL复习三(子查询)

子查询返回的值不止一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,不允许?

SQL 中的错误 - 当子查询跟随 =、!=、<、<=、>、>= 或当子查询用作表达式时,这是不允许的

SQL之子查询