Sql 如何检查左联接上的匹配项是否在另一个表上都有匹配项?
我正在尝试编写一个存储过程,该过程将只返回在左联接右侧未产生任何结果的记录,或者对于在右侧找到的所有记录,只返回在另一个表中具有匹配项的记录的结果集Sql 如何检查左联接上的匹配项是否在另一个表上都有匹配项?,sql,sql-server,tsql,join,left-join,Sql,Sql Server,Tsql,Join,Left Join,我正在尝试编写一个存储过程,该过程将只返回在左联接右侧未产生任何结果的记录,或者对于在右侧找到的所有记录,只返回在另一个表中具有匹配项的记录的结果集 为了说明我试图实现的内容,首先考虑以下表定义: CREATE TYPE [dbo].[TvpDocumentsSent] AS TABLE ( DocumentId INT , RecipientId INT , TransactionId INT ); CREATE TABLE [dbo].[Recipients]
为了说明我试图实现的内容,首先考虑以下表定义:
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在存储过程中用作表值参数。这表明我们正在检查记录
第二个表“收件人”包含所有潜在的文档收件人。值得注意的是,收件人被放置在GroupId指示的组中。组中的所有收件人都应在文档标记为可存档之前收到该文档。顺便说一句,这是我正在努力解决的问题
接下来,RecipientEmails表包含所有可能包含或可能未包含已发送给收件人的文档的电子邮件
后一个表DocumentTransactions存储已发生的所有文档事务的日志。这告诉我DocumentId指示发送了什么文档。尽管此表上没有RecipientId,但TransactionId可用于通过RecipientEmails表将DocumentTransaction跟踪回收件人
我正在努力解决的是如何编写一个查询,该查询只提供通过TVPCoumentsSent传入的记录的一个子集;只有那些在组中没有其他收件人等待文档的人或所有收件人都已收到文档,即DocumentTransactions表中有一条记录,其TransactionId映射回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个收件人可能会收到文档,我如何满足WHERE子句的OR部分,以确保每个人都收到了文档
我尝试了以下操作,但无法正常工作:
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
这是行不通的,因为只要其中一个收件人收到了文档,WHERE子句或WHERE子句的一部分就会通过。假设有5个收件人收到了该文档,但到目前为止只有1个收件人收到了该文档。或将看到1记录的比赛并通过WHERE;这是错误的……它应该强制所有可能的收件人都已收到该文档。不确定下面的示例是否接近。 因为我必须模拟样本数据并猜测预期结果 但在子查询中进行聚合,然后比较总计可能会有所帮助。 或者通过HAVING子句 示例代码段: 返回:
如果您需要全部匹配或不匹配,那么您需要分组并计算总数与匹配的总数。如果匹配的总数为0或等于总数,则必须筛选结果。感谢您的及时回复。我正在尝试这个方法。我会让你知道它是否管用。这管用!通过一些小的改动,这是一个真实事物的淡化表示,它工作得很好。主要的帮助是子查询和通过正确地聚合计数来正确使用GROUP;这就是我一直在努力解决的问题。谢谢@布兰多纳万特嘿,太酷了。我担心我误解了你的意图。因为我发现不管有没有这种要求都有点奇怪。但是,如果它使您更接近您的解决方案,那么一切都很好。是的,它确实需要一些调整,以完全符合完整的要求。出于复杂性的考虑,我再次修改/淡化了我在这里发布的内容。向正确的方向推进是医生的命令。
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