在Where子句中选择带MAX()聚合的语句

时间:2011-11-11 03:02:10

标签: sql sql-server aggregate-functions

我有一个数据库表,每年都会存储会员续订。插入续订记录时,会写一个'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子句中使用,但我无法弄清楚如何构造子查询...或者即使这可以做得更好方式比使用子查询。

4 个答案:

答案 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的人。