避免在子查询中重复 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。
我有一个 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。