2012-02-29 62 views
19

假設我有以下語句,並且內部連接結果爲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 
+0

@bluefeet - 總是?我原以爲這種行爲將會依賴於索引優化。也就是說,除非指定了ORDER BY'子句,否則在SELECT語句中不保證SQL中的排序。而且不幸的是,這種行爲並不是微不足道的。事實上,它不會拋出一個錯誤(對於像'語句返回多個結果'之類的東西),我也會產生錯誤。因爲這個原因,我對'UPDATE'上的'JOIN'有點懷疑,儘管它可能簡化了我在DB2中需要做的一些事情。 – 2012-02-29 16:37:00

+0

您沒有可測試的數據?似乎你應該能夠告訴其他人。 – JeffO 2012-02-29 16:49:29

+0

@ X-Zero,我可以確認在多行上加入一行的更新是非確定性的。這怎麼可能?當我想到計算機時,我認爲是確定性的,這對我來說是不尋常的。 – sooprise 2012-02-29 19:18:09

回答

15

我不認爲這種情況下有規則,你不能依賴於特定的結果。

如果你是一個特定的行後,說最新的一個,你可以使用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 
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提供的解決方案來確保你知道你將選擇什麼樣的價值。

總之,不要相信默認實現,創建自己的。但是,這是一個有趣的學術問題:)

+0

我很想知道爲什麼我爲那些低估了我的答案的人得到了滿意的結果? :) – 2012-02-29 16:51:26

5

通常情況下,您在這種情況下最終會出現在表上物理索引的順序。在實際操作中,您應該將其視爲非確定性的,並且包含將結果縮小爲一行的內容。

+1

絕對,這是我現在學到的東西。我只是很想知道我的問題在我修復之前做了什麼。 – sooprise 2012-02-29 16:49:16

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

爲了完整起見,'UPDATE'也支持'OUTPUT',並且給你「完全控制你正在更新的內容」:主要區別在於這個更新的唯一上下文是* MERGE將會失敗if多行匹配,而不是隨機選擇一個(有效)* – 2014-01-29 13:50:25

+0

我同意簡單的更新語句可以有輸出子句知道更新記錄, 和MERGE查詢將失敗,當我們多次更新單個記錄。 在通常情況下,我們應該嘗試更新一次記錄,因爲更新多次只會保留最後一個值。 – sudhAnsu63 2014-01-30 02:04:22

0

是的,我想出了一個類似的實驗,以賈斯汀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,您會得到相反的結果。我同意上面的海報......奇怪的是,這個操作沒有拋出錯誤信息,因爲它完全隱藏了這個操作IGNORES二級值