返回
巧用子查询和CTE引用HAVING子句中的另一列
mysql
2024-03-05 05:06:15
HAVING 子句中引用另一列的巧妙方法
简介
在查询数据时,HAVING 子句允许我们根据组聚数据进一步过滤结果。然而,如果需要引用另一列来进行过滤,就会遇到 "未知列..." 的错误。本文将探究使用子查询和公用表表达式 (CTE) 来解决此问题的巧妙方法。
问题
想象我们有一个表,其中包含从当前日期开始的过去 12 个月的开始和结束日期。我们想要查询一个包含每个患者最小就诊日期在指定时间段内的患者数量。
方法 1:子查询
SELECT COUNT(b.PatNum)
FROM
(SELECT pl.PatNum
FROM procedurelog pl
WHERE pl.ProcStatus=2
GROUP BY pl.PatNum
HAVING MIN(pl.ProcDate) BETWEEN (
SELECT Start
FROM (
SELECT t.order,
LAST_DAY(CURDATE() - INTERVAL (t.order+1) MONTH) + INTERVAL 1 DAY AS 'Start',
LAST_DAY(CURDATE() - INTERVAL t.order MONTH) AS 'End'
FROM
(SELECT 0 as 'order'
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) t
) AS n
WHERE n.order = 1
)
) b
子查询 的方法利用子查询来创建临时表或视图,其中包含开始和结束日期。HAVING 子句随后引用该子查询的 Start 列。
方法 2:公用表表达式 (CTE)
WITH MonthRange AS (
SELECT t.order,
LAST_DAY(CURDATE() - INTERVAL (t.order+1) MONTH) + INTERVAL 1 DAY AS 'Start',
LAST_DAY(CURDATE() - INTERVAL t.order MONTH) AS 'End'
FROM
(SELECT 0 as 'order'
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) t
)
SELECT COUNT(b.PatNum)
FROM
(SELECT pl.PatNum
FROM procedurelog pl
WHERE pl.ProcStatus=2
GROUP BY pl.PatNum
HAVING MIN(pl.ProcDate) BETWEEN Start AND End
) b
JOIN MonthRange ON MonthRange.order = 1
CTE 方法使用公用表表达式来定义一个命名临时表,其中包含开始和结束日期。HAVING 子句随后引用该 CTE 中的 Start 和 End 列。
结论
通过使用子查询或 CTE,我们可以灵活地引用另一列来过滤 HAVING 子句中的数据。这在需要根据动态计算或从其他表中提取值进行过滤时特别有用。
常见问题解答
-
为什么使用子查询或 CTE 而不是直接引用列?
- 因为直接引用会产生 "未知列..." 的错误。
-
子查询和 CTE 之间有什么区别?
- 子查询是一个嵌套在另一个查询内的单独查询,而 CTE 是一个命名的临时表,可以重复使用。
-
哪种方法更好?
- 这取决于查询的复杂性和性能要求。对于简单的查询,子查询通常更简洁,而对于更复杂的查询,CTE 提供了更好的重用性和可读性。
-
是否可以嵌套子查询或 CTE?
- 可以,但这样做会增加查询的复杂性和执行时间。
-
我还可以使用 HAVING 子句引用哪些其他表达式?
- 聚合函数(例如 SUM、AVG、MAX)、标量函数和 CASE 表达式。