没有连接条件的 T-SQL 匹配记录 1 到 1
Posted
技术标签:
【中文标题】没有连接条件的 T-SQL 匹配记录 1 到 1【英文标题】:T-SQL Match records 1 to 1 without join condition 【发布时间】:2020-09-29 15:51:46 【问题描述】:我有一组实体,它们需要与另一个表中的另一条记录相关联。 当我尝试为要匹配的表输出 Id 时,它不起作用,因为您只能从插入、更新等输出。
DECLARE @SignatureGlobalIdsTbl table (ID int,
CompanyBankAccountId int);
INSERT INTO GlobalIds (TypeId)
-- I Cannot output cba.Id into the table since its not from inserted
OUTPUT Inserted.Id,
cba.Id
INTO @SignatureGlobalIdsTbl (ID,
CompanyBankAccountId)
SELECT (@DocumentsGlobalTypeKey)
FROM CompanyBankAccounts cba
INNER JOIN Companies c ON c.CompanyId = cba.CompanyId
WHERE SignatureDocumentId IS NULL
AND (SignatureFile IS NOT NULL
AND SignatureFile != '');
INSERT INTO Documents (DocumentPath,
DocumentType,
DocumentIsExternal,
OwnerGlobalId,
OwnerGlobalTypeID,
DocumentName,
Extension,
GlobalId)
SELECT SignatureFile,
@SignatureDocumentTypeKey,
1,
CompanyGlobalId,
@OwnerGlobalTypeKey,
[dbo].[fnGetFileNameWithoutExtension](SignatureFile),
[dbo].[fnGetFileExtension](SignatureFile),
documentGlobalId
FROM (SELECT c.GlobalId AS CompanyGlobalId,
cba.*,
s.ID AS documentGlobalId
FROM CompanyBankAccounts cba
INNER JOIN Companies c ON c.CompanyId = cba.CompanyId
CROSS JOIN @SignatureGlobalIdsTbl s) info
WHERE SignatureDocumentId IS NULL
AND (SignatureFile IS NOT NULL
AND SignatureFile != '');
我尝试使用交叉连接来防止笛卡尔生产,但没有奏效。我还尝试将行号输出为某个值,但我也无法将其存储在表中。
如果我有两个单独的查询返回相同数量的记录,如何在不创建笛卡尔生产的情况下将记录配对?
【问题讨论】:
仅供参考,就像任何书面语言(不仅仅是代码,还有“普通”语言),良好的格式使您的写作(代码)更具可读性和易于理解.你真的应该花时间来确保你编写的代码是像样的。空格和换行符很重要。 至于你的问题,“不起作用”是什么意思? @Larnu 我无法从选择中输出,您只能从插入或更新的记录中输出。我只是在寻找一种简单的方法来配对两个没有连接条件的单独查询 【参考方案1】:'当我尝试为表格输出 Id 时......它不起作用。'
这似乎是因为您要输出的列之一实际上并不是插入的一部分。这是一个烦人的问题,我希望 SQL Server 允许我们这样做。
可能有人比我有更好的答案,但我通常处理这个问题的方式是
为我要插入的数据创建一个临时表/等,其中包含 ID 列(开始为空白) 插入正确数量的行,并将 ID 取出到另一个临时表中, 在原始临时表中适当分配 ID 返回并使用所需的任何其他数据更新插入的行(尽管此处可能不需要,因为您只是插入了一个常量)这样做的目的是标记/准备好 ID 以供您使用,然后根据需要将它们分配给您的数据,然后用数据填写表格。它相对简单,尽管它确实执行 2 次表命中而不是 1 次。
还可以考虑在事务中完成所有操作以保持数据一致(尽管这里也可能不需要)。
如何将记录配对?
不幸的是,交叉连接会增加行数(左侧的行数乘以右侧的行数)。它在某些情况下很有用,但可能不是在这里。
我建议您在执行上述插入操作时,在临时表中获得一个标识符(例如 companyID)并加入该标识符。
如果您没有有匹配的记录并且只想按顺序分配它们,您可以使用类似于我在另一个最近的问题How to update multiple rows in a temp table with multiple values from another table using only one ID common between them?的答案的答案
补充说明
出于性能原因,我建议避免使用表变量(例如,DECLARE @yourtable TABLE)并改用临时表(CREATE TABLE #yourtable)。如果它只是少量的行,那没关系,但随着 SQL Server 假设表变量只有 1 行而变大,情况会变得更糟 在您的底部语句中,为什么 FROM 子句中有 SELECT 语句?难道你不能去掉那个 select 语句,让 FROM 子句列出你想要的表吗?【讨论】:
output
上的 merge
子句可以访问 source
和 target
列。一个经典的问题是复制父/子层次结构:子级被复制并分配了新的 id,但原始父级 id 不再适用。它需要第二遍才能将旧的父 ID 映射到新值。使用merge ... insert ... output...
后跟update
来解决问题的特殊示例隐藏在this 答案中。【参考方案2】:
我想出了一种通过使用合并语句来访问输出的方法。
DECLARE @LogoGlobalIdsTbl TABLE (ID INT, companyBankAccountID INT)
MERGE GlobalIds
USING
(
SELECT (cba.CompanyBankAccountId)
FROM CompanyBankAccounts cba
INNER JOIN Companies c on c.CompanyId = cba.CompanyId
WHERE cba.LogoDocumentId IS NULL AND (cba.LogoFile IS NOT NUll AND cba.LogoFile != '')
) src ON (1=0)
WHEN NOT MATCHED
THEN INSERT ( TypeId )
VALUES (@DocumentsGlobalTypeKey)
OUTPUT [INSERTED].[Id], src.CompanyBankAccountId
INTO @LogoGlobalIdsTbl;
【讨论】:
以上是关于没有连接条件的 T-SQL 匹配记录 1 到 1的主要内容,如果未能解决你的问题,请参考以下文章