Sql 在多行上更新和联接,使用哪一行的值?

Sql 在多行上更新和联接,使用哪一行的值?,sql,sql-server,sql-server-2008-r2,Sql,Sql Server,Sql Server 2008 R2,假设我有下面的语句,内部连接结果在3行中,其中a.Id=b.Id,但这3行中的每一行都有不同的b.Value。由于只更新表A中的一行,更新中使用了3个值中的哪一个 UPDATE a SET a.Value = b.Value FROM tableA AS a INNER JOIN tableB as b ON a.Id = b.Id 我不认为这个案件有规则,你不能依赖于一个特定的结果 如果要查找某一行,比如最新的一行,可以使用apply,如: 下面是我使用SQLServer2008的想法 -

假设我有下面的语句,内部连接结果在3行中,其中a.Id=b.Id,但这3行中的每一行都有不同的b.Value。由于只更新表A中的一行,更新中使用了3个值中的哪一个

UPDATE a
SET a.Value = b.Value
FROM tableA AS a
INNER JOIN tableB as b 
ON a.Id = b.Id

我不认为这个案件有规则,你不能依赖于一个特定的结果

如果要查找某一行,比如最新的一行,可以使用apply,如:


下面是我使用SQLServer2008的想法

--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
似乎每次从b选择*的第1行时,它都使用基本选择的顶部值。因此,这可能取决于索引。但是,我不会依赖SQL设置的实现,因为这可能会改变。相反,我建议使用Andomar提出的解决方案,以确保您知道要选择什么价值


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

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

在我的情况下,更新多个记录的最佳选择是使用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;
示例:With 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.

是的,我做了一个和贾斯汀·皮奥尼相似的实验:

    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,您将得到相反的结果。我同意上面的海报…非常奇怪的是,这个操作没有抛出错误消息,因为它完全隐藏了这个操作忽略次要值

@bluefeet-总是吗?我原以为这种行为会依赖于索引优化。也就是说,除非指定ORDERBY子句,否则SELECT语句中不保证SQL中的排序。不幸的是,这种行为并不容易测试。它不会为“语句返回了多个结果”之类的内容抛出错误,这一事实也让我感到困惑。出于这个原因,我对更新时的连接有点怀疑,尽管它可能简化了我在DB2中需要做的一些事情?看起来你应该能够告诉其他人。@X-Zero,我可以确认一行连接多行的更新是不确定的。这怎么可能?当我想到计算机时,我认为确定性,这对我来说有点不寻常。@Jeff-我在iSeries上的DB2上,它不支持这种语法。但更大的问题是,您必须运行测试,直到优化器选择一条不同的路径,这是您几乎无法直接控制的——您可以尝试影响它创建标识的方式以及数据的分布,但这并不能保证它实际使用的顺序。你可以在两台不同的机器上使用完全相同的数据集,得到两个不同的结果,仅仅因为一台机器有更多的ram。@sooprise-计算机总是确定性的,并且没有办法改变这种行为-看看生成“随机”数的困难。这包括优化连接顺序;但是,它似乎是不确定的,因为在运行语句时,您没有或能够查看系统考虑的所有可用因素。通常,这是一件好事——它允许DBMS选择“更好”的路径,而无需通知或以其他方式影响您。这相当于在排序例程之间进行选择的函数——用户只需要对数据进行排序。当然,这是我现在学到的东西。我只是想知道在我修正之前我的问题在做什么。我也是,@soopriseI很想知道为什么我得到了那些否决我答案的人的否决票为了完整性起见,UPDATE也支持输出,并让您完全控制更新内容:此仅更新上下文中的关键区别在于,如果多行匹配,合并将失败,而不是随机选择一行。我同意简单的UPDATE语句可以使用OUTPUT子句来知道更新的记录,当我们多次更新单个记录时,合并查询将失败。在通常情况下,我们应该尝试一次更新一条记录,因为多次更新只会保留最后一个值。
    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