没有连接条件的 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 子句可以访问 sourcetarget 列。一个经典的问题是复制父/子层次结构:子级被复制并分配了新的 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的主要内容,如果未能解决你的问题,请参考以下文章

Mysql高阶

7-10外连接查询

您将如何使用 T-SQL 获得满足条件的顺序/连续记录的最大/最大计数

SQL 连接的补充?需要 T-SQL 帮助

MySQL数据库(21):连接查询 join

mysql连接查询