代码之家  ›  专栏  ›  技术社区  ›  Brandon Avant

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

  •  0
  • Brandon Avant  · 技术社区  · 8 年前

    我正在尝试编写一个存储过程,该过程将只返回在 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 可用于通过 收件人电子邮件 桌子。

    我正在努力的是如何编写一个查询,该查询只提供通过 TvpDcoumentsSent ;只有那些没有其他收件人在组中等待文档的人 所有收件人都已收到该文档(即 单据交易 谁的桌子 事务ID 映射回中的记录 RecipientEmail 其收件人有资格使用此文档)。

    我现在想到的是这个(注:我知道我正在使用 TVPdocumentsSent公司 作为一个表格而不是tvp在下面的查询。我这样做是为了简化我的解释。

    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 ??????
    

    请记住,有N个收件人可能会收到文档,我如何完成 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个收到了它。那个 或者 将看到1个记录的匹配并通过 在哪里? ;那是错误的……它应该强制执行 全部 潜在收件人已收到文档。

    1 回复  |  直到 8 年前
        1
  •  1
  •   LukStorms    8 年前

    不确定下面的例子是否接近。
    因为我必须模拟样本数据并猜测预期结果。

    但是,在子查询中进行聚合,然后比较总数可能会有帮助。
    (或通过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
    
    推荐文章