根据另一个表中值的存在排除聚合函数中的记录

时间:2015-07-09 13:43:13

标签: sql sql-server

我正在编写一个基于邮政编码生成统计信息的查询,我需要能够计算一系列邮政编码中的匹配记录数,除非它们存在于辅助表中。这是一个更大的查询的一部分,我需要以列式格式而不是单独的行来记录每个邮政编码的记录,这个最小的例子演示了我尝试过的内容:

CREATE TABLE #People
(
    Name nvarchar(10),
    Postcode int
)

INSERT INTO #People VALUES ('Adam', 2000)
INSERT INTO #People VALUES ('John', 2001)
INSERT INTO #People VALUES ('Paul', 2001)
INSERT INTO #People VALUES ('Peter', 2099)
INSERT INTO #People VALUES ('Tom', 4000)

CREATE TABLE #PostcodesToIgnore
(
    Postcode int
)

INSERT INTO #PostcodesToIgnore VALUES (2099)

SELECT SUM(CASE WHEN PostCode BETWEEN 2000 AND 2099 THEN 1 ELSE 0 END) FROM #People

SELECT SUM(CASE WHEN PostCode BETWEEN 2000 AND 2099 
    AND PostCode NOT IN (SELECT PostCode FROM #PostcodesToIgnore) THEN 1 ELSE 0 END) 
    FROM #People

计算范围内所有邮政编码的第一个查询有效但第二个查询失败并显示错误:

  

无法对包含聚合或子查询的表达式执行聚合函数。

虽然我可以重构查询以将外部选择中的所有条件包含到每个子选择中,但在真实查询中有相当多的标准,所以我希望可能有更优雅的方法来解决它?

4 个答案:

答案 0 :(得分:3)

您可以使用左连接。

SELECT 
SUM
(
    CASE WHEN PostCode BETWEEN 2000 AND 2099 
        AND pcti.PostCode is null 
    THEN 1 
    ELSE 0 
    END
)
FROM #People p
left join #PostcodesToIgnore pcti on pcti.PostCode = p.PostCode

答案 1 :(得分:2)

您可以删除SUM并将查询推送到派生表或CTE。

以下作品

SELECT SUM(PostCodeFlag)
FROM   (SELECT CASE
                 WHEN PostCode BETWEEN 2000 AND 2099
                      AND PostCode NOT IN (SELECT PostCode
                                           FROM   #PostcodesToIgnore) THEN 1
                 ELSE 0
               END AS PostCodeFlag
        FROM   #People) T 

答案 2 :(得分:2)

这样的事情: 使用CTE预先准备数据,然后进行简单的分组计数。 或者您可以查看OVER(https://msdn.microsoft.com/en-us/library/ms189461.aspx

{{1}}

答案 3 :(得分:1)

FROM #people WHERE邮政编码不在(...)。

事实上,您似乎根本不需要任何CASE,您可以在FROM中指定所有谓词。

或者我错过了什么?