如何检查左联接ALL上的匹配项在另一张表上是否有匹配项?

时间:2018-07-11 07:16:48

标签: sql sql-server tsql join left-join

我正在尝试编写一个存储过程,该存储过程将仅返回在{strong> all 或右侧未产生任何结果的记录。在右侧找到的记录的“ strong>”中,仅返回在另一个表中具有匹配项的结果集。

为说明我要达到的目的,请首先考虑以下表格定义:

LEFT JOIN

第一个表CREATE TYPE [dbo].[TvpDocumentsSent] AS TABLE ( DocumentId INT , RecipientId INT , TransactionId INT ); CREATE TABLE [dbo].[Recipients] ( RecipientId INT , GroupId INT ) CREATE TABLE [dbo[.[RecipientEmails] ( RecipientId INT , TransactionID INT ) CREATE TABLE [dbo].[DocumentTransactions] ( TransactionId INT , DocumentId INT ) 在存储过程中用作表值参数。它表示我们正在检查的记录。

第二张表TvpDocumentsSent包含所有潜在的文档接收者。值得注意的是,收件人被分组放置(由Recipients表示)。群组中的所有收件人都应在文档之前收到文档,该文档被标记为可存档。顺便说一句,这就是我正在努力的部分。

接下来,GroupId表将容纳已发送给收件人的所有电子邮件(可能包含也可能不包含文档)。

后一个表RecipientEmails存储已发生的所有文档交易的日志。这告诉我发送了什么文档(由DocumentTransactions指示)。尽管此表上没有没有 DocumentId,但是RecipientId可用于通过TransactionId表将DocumentTransaction追溯到收件人。

我正在苦苦挣扎的是如何编写一个查询,该查询只给我通过RecipientEmails传递的记录的子集;只有那些没有其他收件人等待组中的人或的所有收件人都已收到该文件(即TvpDcoumentsSent表中有一条记录,其中{{1} }映射回DocumentTransactions中的记录,该记录的收件人有资格使用此文档。

到目前为止,我想的是这个(注意:我知道我在下面的查询中使用TransactionId作为表而不是TVP。我这样做是为了简化说明)

RecipientEmail

请记住,可能有n位收件人可能接收该文档,我如何履行TvpDocumentsSent子句的SELECT SNT.DocumentId FROM [dbo].[TvpDocumentsSent] AS SNT INNER JOIN [dbo].[Recipients] AS RCP ON -- The recipient who recieved the document during this transaction. RCP.RecipientId = SNT.RecipientId LEFT JOIN [dbo].[Recipients] AS OTHR_RCP ON -- Other recipients who may have already received the document or could later. RCP.GroupId = OTHR_RCP.GroupId AND RCP.RecipientId != OTHR_RCP.RecipientId WHERE OTHR_RCP.RecipientId IS NULL OR ?????? 部分以确保每个人都收到了文档?

我尝试了以下操作,但无法正常工作:

OR

那是行不通的,因为只要收件人之一收到了文档,WHERE子句的SELECT SNT.DocumentId FROM [dbo].[TvpDocumentsSent] AS SNT INNER JOIN [dbo].[Recipients] AS RCP ON -- The recipient who recieved the document during this transaction. RCP.RecipientId = SNT.RecipientId LEFT JOIN [dbo].[Recipients] AS OTHR_RCP ON -- Other recipients who may have already received the document or could later. RCP.GroupId = OTHR_RCP.GroupId AND RCP.RecipientId != OTHR_RCP.RecipientId LEFT JOIN [dbo].[DocumentTransactions] AS DT ON SNT.TransactionId = DT.TransactionId WHERE OTHR_RCP.RecipientId IS NULL OR DT.DocumentId IS NOT NULL 部分就会通过。假设应该有5位收件人收到了文档,但到目前为止只有1位收到了。 OR将看到1条记录的匹配并通过WHERE;错了...应该强制所有潜在收件人已收到文档。

1 个答案:

答案 0 :(得分:1)

不确定下面的示例是否接近。
由于我不得不模拟样本数据并猜测预期结果。

但是在子查询中进行汇总,然后比较总计可能会在这里有所帮助。
(或通过HAVING子句)

示例代码段

declare @Recipients table (RecipientId int primary key, GroupId int);
declare @DocumentTransactions table (TransactionId int primary key, DocumentId int);
declare @DocumentsSent table (DocumentId int, RecipientId int, TransactionId int);
declare @RecipientEmails table (RecipientId int, TransactionID int);

insert into @Recipients (RecipientId, GroupId) values 
 (201,1),(202,1),(203,1),(204,2),(205,2),(206,2);
insert into @DocumentTransactions (TransactionId, DocumentId) values 
 (301,101),(302,101),(303,101),(304,102),(305,102),(306,102);
insert into @DocumentsSent (DocumentId, RecipientId, TransactionId) values 
 (101,201,301),(101,202,302),(101,203,303)
,(102,204,304),(102,205,305),(102,206,306);
insert into @RecipientEmails (RecipientId, TransactionId) values 
 (201,301),(202,302),(203,303)
,(204,304);

SELECT DocumentId 
FROM
(
    SELECT 
     tr.DocumentId, 
     rcpt.GroupId, 
     count(distinct sent.RecipientId) AS TotalSent,
     count(distinct rcptmail.RecipientId) AS TotalRcptEmail
    FROM @DocumentsSent AS sent
    LEFT JOIN @Recipients AS rcpt ON rcpt.RecipientId = sent.RecipientId
    LEFT JOIN @DocumentTransactions AS tr
           ON (tr.TransactionId = sent.TransactionId AND tr.DocumentId = sent.DocumentId)
    LEFT JOIN @RecipientEmails AS rcptmail
           ON (rcptmail.TransactionId = sent.TransactionId AND rcptmail.RecipientId = sent.RecipientId)
    GROUP BY tr.DocumentId, rcpt.GroupId
) AS q
WHERE (TotalSent = TotalRcptEmail OR (TotalSent > 0 AND TotalRcptEmail = 0))
GROUP BY DocumentId;

/*
SELECT
 tr.TransactionId, 
 sent.DocumentId, 
 sent.RecipientId AS RecipientIdSent, 
 rcpt.GroupId AS GroupIdRcpt, 
 rcpt.RecipientId AS RecipientIdRcpt, 
 rcptmail.RecipientId AS RecipientIdEmail
FROM @DocumentsSent AS sent
LEFT JOIN @Recipients AS rcpt ON rcpt.RecipientId = sent.RecipientId
LEFT JOIN @DocumentTransactions AS tr
           ON (tr.TransactionId = sent.TransactionId AND tr.DocumentId = sent.DocumentId)
LEFT JOIN @RecipientEmails AS rcptmail
           ON (rcptmail.TransactionId = sent.TransactionId AND rcptmail.RecipientId = sent.RecipientId);
*/

返回:

DocumentId
----------
       101