简体   繁体   English

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

[英]Excluding records within an aggregate function based on presence of value in another table

I'm writing a query that generates statistics based on postcodes and I need to be able to count the number of matching records that are within a range of postcodes except when they exist in a secondary table. 我正在写一个查询,该查询根据邮政编码生成统计信息,并且我需要能够计算一系列邮政编码内的匹配记录数,除非它们存在于辅助表中。 This is part of a larger query and I need the count of records for each postcodes in columnar format rather than as separate rows and this minimal example demonstrates what I've attempted: 这是一个较大查询的一部分,我需要每个邮政编码的记录数都是列式格式,而不是单独的行,这个最小的示例演示了我尝试过的内容:

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

The first query that counts all postcodes within the range works but the second one fails with the error: 第一个查询计算该范围内的所有邮政编码,但第二个查询失败,并显示以下错误:

Cannot perform an aggregate function on an expression containing an aggregate or a subquery. 无法对包含聚合或子查询的表达式执行聚合功能。

While I could refactor the query to include all the criteria from the outer select into each subselect there are quite a few criteria in the real query so I was hoping there might be a more elegant way to go about it? 虽然我可以重构查询以将外部选择中的所有条件包括到每个子选择中,但实际查询中有很多条件,所以我希望可以采用一种更优雅的方法来解决?

You could use a left join instead. 您可以改用左联接。

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

You could remove the SUM and push the query into a derived table or CTE. 您可以删除SUM并将查询推送到派生表或CTE中。

The following works 以下作品

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 

Something like this: Use a CTE to pre-prepare your data, then do a simple grouped count. 像这样:使用CTE预先准备数据,然后进行简单的分组计数。 Or you could have a look on OVER ( https://msdn.microsoft.com/en-us/library/ms189461.aspx ) 或者您可以查看OVER( https://msdn.microsoft.com/zh-cn/library/ms189461.aspx

WITH myCTE AS
(
    SELECT Name,Postcode FROM #People
    WHERE Postcode NOT IN (SELECT Postcode FROM #PostcodesToIgnore)
)
SELECT Postcode, Count(Name) 
FROM myCTE
GROUP BY Postcode       

FROM #people WHERE postcode not in (...). 来自#people,邮政编码不在(...)中。

In fact, it looks like you just don't need any CASE at all and you can specify all of your predicates in the FROM. 实际上,看起来您根本不需要任何CASE,并且可以在FROM中指定所有谓词。

Or am I missing something ? 还是我错过了什么?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM