SQL只查找没有匹配记录的表中的数据

时间:2016-04-13 15:19:40

标签: sql sql-server

我想在一个表中找到基于两个字段(ContractState,TransID)没有匹配记录的数据。

例如,假设这个数据集(实际上,所有数据集包含数百条记录,我只包括一些):

AccountNbr  ContractState   TransID     Product
3335477     AL              80079       DPPO, DHMO
3335477     AL              80080       PPO
3335477     AR              80079       DPPO, DHMO
3335477     AR              80080       PPO

这应返回0条记录,因为AL有2条记录(每条TransID一条记录),AR记录为2条记录。

但是,给定以下数据集:

AccountNbr  ContractState   TransID     Product
3335477     AL              80079       DPPO, DHMO
3335477     AL              80080       PPO
3335477     DE              80079       DHMO
3335477     WA              80080       DHMO

我想只返回以下数据集:

AccountNbr  ContractState   TransID     Product
3335477     DE              80079       DHMO
3335477     WA              80080       DHMO

因为每个州只有1个TransID。

我有这段代码,但它也包含匹配数据的记录:

SELECT 
   'tblSQLContractState' as TableName, 
   TransID, 
   ContractState, 
   Product, 
   COUNT(*) AS [NumOfMessage]
FROM tblSQLContractState  
WHERE   TransID IN (80079, 80080)
GROUP BY 
   TransID, 
   ContractState, 
   Product
HAVING COUNT(*) = 1

3 个答案:

答案 0 :(得分:1)

使用窗口功能。对于您提供的数据,这应该有效:

select cs.*
from (select cs.*,
             count(*) over (partition by AccountNbr, ContractState) as cnt
      from tblSQLContractState cs
      where TransID IN (80079, 80080)
     ) cs
where cnt = 1;

答案 1 :(得分:1)

您可以使用NOT EXISTS选择记录中包含此TransId但不包含此ContractState的记录的记录:

SELECT 
   'tblSQLContractState' as TableName, 
    cs.AccountNbr,  cs.ContractState, cs.TransID,  cs.Product
FROM tblSQLContractState cs
WHERE   cs.TransID IN (80079, 80080)
AND NOT EXISTS                                  -- no other record
(
    SELECT 1 FROM tblSQLContractState cs2
    WHERE cs2.TransID <> cs.TransID             -- with other TransId
      AND cs2.ContractState = cs.ContractState  -- and this  ContractState
)

NOT EXISTS在SQL-Server中非常有效,可以轻松修改/扩展。另一个优点是您可以选择所有列而不是GROUP BY

答案 2 :(得分:1)

您的查询已结束。但是你想要每个ContractState一个结果行 - 而且你也想知道这个属性的记录数(聚合COUNT(*))。因此,请从Product子句中删除TransIDGROUP BY

由于它只是您想要显示的行数为1的组合,因此您可以使用MIN(Product)MAX(Product)来获取相关产品。与TransID相同。

SELECT 
   'tblSQLContractState' as TableName, 
   MAX(TransID) AS TransID, 
   ContractState, 
   MAX(Product) AS Product, 
   COUNT(*) AS [NumOfMessage]
FROM tblSQLContractState  
WHERE   TransID IN (80079, 80080)
GROUP BY ContractState
HAVING COUNT(*) = 1;

戈登的答案更直接,更具普遍用途。在我看来,这是解决这个问题的自然方式。我只想表明你的查询非常接近: - )

更新:蒂姆的回答也非常好。 (当我输入我的时候不存在。)他是对的;所有你想知道的是,是否存在ContractState的另一条记录,因此一个exists子句是合适的。