我有一个数据库表,每年都会存储会员续订。插入续订记录时,会写一个'expiryDate'列,其中包含日期(31/8 / [nextyear])。
因此,作为一个例子,假设一个成员在2007,2008和2009年更新了memberID = 99,他将有3条记录(每年一条记录),每条记录中都记录了“expiryDate”。如果我做了
SELECT MAX(YEAR(expiryDate)) as maxExpiry
FROM renewals
WHERE memberID = 99
......我将回归2010年。
我想要做的是返回MAX(YEAR(expiryDate))
是给定年份的所有记录...例如,
SELECT *
FROM renewals
WHERE MAX(YEAR(expiryDate)) = '2010';
此查询不起作用,因为聚合不能在子查询外的where子句中使用,但我无法弄清楚如何构造子查询...或者即使这可以做得更好方式比使用子查询。
答案 0 :(得分:5)
基于聚合列的谓词使用HAVING
子句,而不是WHERE
。
如果您只需要memberID,这很简单:
SELECT memberID
FROM renewals
GROUP BY memberID
HAVING MAX(YEAR(expiryDate)) = 2010
如果您需要从该表中获取其他列,您也可以将其作为子查询执行,即:
SELECT * FROM members
WHERE memberID IN ( <<previous query>> )
<强>更新强>
这是正确的,因为@OMG Ponies指出,如果您需要从renewals
中的那一行中选择其他列,这还不够。如果需要,您可以使用:
SELECT * FROM renewals
WHERE memberID IN ( SELECT memberID FROM renewals
GROUP BY memberID HAVING MAX(YEAR(expiryDate)) = 2010 )
AND YEAR(expiryDate) == 2010
答案 1 :(得分:1)
使用GROUP BY
SELECT memberID, MAX(YEAR(expiryDate))
FROM renewals
GROUP BY memberID
HAVING MAX(YEAR(expiryDate)) = 2010
答案 2 :(得分:1)
对于SQL Server 2005+,请使用:
WITH cte AS (
SELECT r.*,
ROW_NUMBER() OVER (PARTITION BY r.memberid
ORDER BY r.expirydate DESC) AS rnk
FROM RENEWALS r)
SELECT c.*
FROM cte c
WHERE c.rnk = 1
AND YEAR(c.expirydate) = 2010
CTE不是2005年以上的真正原因 - 它是ROW_NUMBER
的使用,因为它可以重写为不使用CTE。
子查询的问题是,获取memberid
(就像您在其他答案中看到的那样)不足以加入RENEWALS
表的副本。您将获得这些成员的所有记录,但仍需要过滤掉您要查找的内容。
答案 3 :(得分:1)
这个问题现在已经有几个月了,并且有一个可接受的答案以及两个更有效的答案。不过,我还在补充另一个:
SELECT *
FROM renewals r
WHERE expiryDate >= '20100101' -- unambiguous input format with any locale!
AND expiryDate < '20110101'
AND NOT EXISTS (
SELECT *
FROM renewals r0
WHERE r0.memberID = r.memberID
AND r0.expiryDate > r.expiryDate
);
为什么?以前的所有答案对于大表来说都会很慢,因为他们无法在expiryDate
上使用索引。 这个可以。 Aaron Bertrand(也在SO上活跃)写了一篇关于here主题的博客 - 它同意what I keep preaching对PostgreSQL的细节。
就性能而言,能够使用索引比这里的查询样式的其他细节更重要。
此外,此查询会阻止同一成员的多行。它仅返回每个成员2010年的最新行 - 如果该年份应该有多个条目。不应该根据描述发生,但很容易有例外。我认为这就是我们所需要的。 @OMG小马的回答是到目前为止唯一回答这个细节的答案。具有讽刺意味的是,直到现在,它也是唯一一个没有upvote的人。