更新并连接多行,使用哪一行的值?

问题描述 投票:0回答:6

假设我有以下语句,内部联接产生 3 行,其中 a.Id = b.Id,但 3 行中的每一行都有不同的 b.Value。 由于只更新 tableA 中的一行,因此更新中使用 3 个值中的哪一个?

UPDATE a
SET a.Value = b.Value
FROM tableA AS a
INNER JOIN tableB as b 
ON a.Id = b.Id
sql sql-server sql-server-2008-r2
6个回答
20
投票

我认为这种情况没有规则,你不能依赖特定的结果。

如果您要查找特定行,例如最新的一行,您可以使用

apply
,例如:

UPDATE  a
SET     a.Value = b.Value
FROM    tableA AS a
CROSS APPLY
        (
        select  top 1 *
        from    tableB as b
        where   b.id = a.id
        order by
                DateColumn desc
        ) as b

9
投票

通常,在这种情况下,您最终得到的结果是按表上物理索引的顺序出现的第一行。在实际实践中,您应该将其视为不确定性,并包含一些将结果缩小到一行的内容。


2
投票

这是我使用 SQL Server 2008 得出的结论

--drop table #b
--drop table #a
select 1 as id, 2 as value
into #a

select 1 as id, 5 as value
into #b

insert into #b
select 1, 3

insert into #b
select 1, 6

select * from #a
select * from #b

UPDATE #a 
SET #a.Value = #b.Value
FROM #a
INNER JOIN #b 
ON #a.Id = #b.Id

它似乎每次都使用基本选择的顶部值(select * from #b 的第 1 行)。因此,这可能取决于索引。然而,我不会依赖 SQL 的实现集,因为它有可能改变。相反,我建议使用 Andomar 提供的解决方案来确保您知道要选择什么值。

简而言之,不要相信默认实现,而是创建自己的实现。但是,这是一个有趣的学术问题:)


2
投票

就我而言,更新多条记录的最佳选择是使用合并查询(从 SQL Server 2008 支持),在此查询中您可以完全控制要更新的内容。 您也可以使用输出查询进行进一步处理。

示例:不带输出子句(仅更新)

;WITH cteB AS
( SELECT Id, Col1, Col2, Col3  
  FROM B WHERE Id > 10  ---- Select Multiple records
)
MERGE A
USING cteB
ON(A.Id = cteB.Id) -- Update condition
WHEN MATCHED THEN UPDATE
SET  
A.Col1 = cteB.Col1,  --Note: Update condition i.e; A.Id = cteB.Id cant appear here   again.
A.Col2 = cteB.Col2,
A.Col3 = cteB.Col3;

示例:使用 OputPut 子句

CREATE TABLE #TempOutPutTable
  {
  PkId INT NOT NULL,
  Col1 VARCHAR(50),
  Col2 VARCHAR(50)
  }

;WITH cteB AS
( SELECT Id, Col1, Col2, Col3
FROM B WHERE Id > 10
)
MERGE A
USING cteB
ON(A.Id = cteB.Id)
WHEN MATCHED THEN UPDATE
SET  
A.Col1 = cteB.Col1, 
A.Col2 = cteB.Col2,
A.Col3 = cteB.Col3
OUTPUT 
 INSERTED.Id, cteB.Col1, A.Col2 INTO #TempOutPutTable;

--Do what ever you want with the data in temporary table
SELECT * FROM #TempOutPutTable; -- you can check here which records are updated.

0
投票

是的,我想出了一个与 Justin Pihony 类似的实验:

    IF OBJECT_ID('tempdb..#test') IS NOT NULL DROP TABLE #test ;
SELECT 
1 AS Name, 0 AS value 
INTO #test

IF OBJECT_ID('tempdb..#compare') IS NOT NULL DROP TABLE #compare ;
SELECT 1 AS name, 1 AS value
INTO #compare
INSERT INTO #compare
SELECT 1 AS name, 0 AS value;

SELECT * FROM #test
SELECT * FROM #compare

UPDATE t
SET t.value = c.value
FROM #test t
INNER JOIN #compare c
    ON t.Name = c.name

占据右侧比较表中的最上面一行。您可以将 #compare.value 值反转为 0 和 1,您将得到相反的结果。 我同意上面的海报...很奇怪的是,这个操作没有抛出错误消息,因为它完全隐藏这个操作忽略辅助值


0
投票

合并到表1 A 使用表2 U ON(较低(修剪(A.src_OwnerAlias))=较低(修剪(U.ALIAS))) 当匹配时 更新集 A.scout_OwnerId = U.ID WHERE lower(trim(A.src_OwnerAlias)) = lower(trim(U.ALIAS)) AND U.SCT_COUNTRY__C ='DE' AND U.Org_name = 'EMEA2QA' ;

© www.soinside.com 2019 - 2024. All rights reserved.