避免在子查询中重复 WHERE 条件

Avoid repeating WHERE conditions in subqueries

我有一个 SQL 查询:

SELECT * FROM a 
INNER JOIN
    (
        SELECT date, MAX(z) FROM a
        WHERE a.x = "foo" AND a.y IN (1, 2, 3)
        GROUP BY z
    ) b
    ON a.date = b.date
WHERE a.x = "foo" AND a.y IN (1, 2, 3)

我的问题是,有这个重复的 WHERE 条件是否有代码味道?也许答案取决于特定的上下文,但我希望得到一个“一般”的答案,或者一些关于如何让它变得更好的指示。

使用window 函数。如果你想要每个 z 的最大日期:

SELECT a.*,
       MAX(z) OVER (PARTITION BY date)
FROM a 
WHERE a.x = "foo" AND a.y IN (1, 2, 3);

假设您想要 date 的最大值 z,那么您 do 需要两个 where 子句,因为没有它的 where 子句子查询将 return max z for any row for the date, not the max z for the您正在查询的行。

您的查询有一个小错误:子查询应该使用 GROUP BY date 而不是 GROUP BY z:

SELECT * FROM a 
INNER JOIN
(
    SELECT date, MAX(z) FROM a
    WHERE a.x = "foo" AND a.y IN (1, 2, 3)
    GROUP BY date
) b
ON a.date = b.date
WHERE a.x = "foo" AND a.y IN (1, 2, 3)

这个子查询:

SELECT date, MAX(z) FROM a
WHERE a.x = "foo" AND a.y IN (1, 2, 3)
GROUP BY z

在 SQLite 中是允许的,但它在大多数其他数据库中是无效的,因为虽然你 GROUP BY z 你也在 z 上聚合并且你选择了非聚合列 date.

无论如何,此查询所做的是在 WHERE 子句的条件下过滤 table,然后 return 为每个不同的 z 过滤 1 行(因为 MAX(z) 实际上等于 z 在它自己的组中)具有任意值 date.

然后在子查询 return 的任意日期将 table 与此子查询连接起来。这一切都非常不清楚您心目中的预期输出是什么。

但是,如果您担心的是重复条件,那么我应该说,如果您能避免它们总是更好。

在你的情况下有一个CTE的解决方案,因为在你的主查询和你的子查询中,你正在做同样的过滤,所以你只对满足这些条件的行感兴趣:

a.x = "foo" AND a.y IN (1, 2, 3)

因此您可以得到与此相同的结果:

WITH cte AS (
  SELECT * FROM a 
  WHERE a.x = "foo" AND a.y IN (1, 2, 3)
)
SELECT * 
FROM cte c
INNER JOIN (
  SELECT date, MAX(z) FROM cte
  GROUP BY z
) b ON b.date = c.date 

参见demo