2012-02-16 16 views
3

Itzik Ben-Gan在他的書'InsideMicrosoft®SQLServer®2008:T-SQL'中解釋了Appoach A是如何工作的,因爲SQL Server的未公開行爲在其中執行從SELECTGroup String Concatenation:哪種方法更有保證?

備受推崇collegue和DB大師建議,B方法保證工作。他的論點是基於COALESCECAST的「價值擴張」方法的遞歸性質。

實際上,我不知道「值擴展」是指什麼(除了它將一個值賦值給另一個值)或它如何應用於這個問題?也許他誤解了?是的,在某種意義上,COALESCE是遞歸的,但據我所知,它是無關緊要的,並且由於多個賦值的未記錄行爲而產生期望的結果。

他是否正確?請不要「使用FOR XML PATH」代替「答案!

解決方法A

DECLARE @output VARCHAR(100); 
SET @output = ''; 

SELECT @output = @output + CAST(COL_VCHAR AS VARCHAR(10)) + ';' 
FROM someTable; 

B方法

DECLARE @output VARCHAR(100); 
SELECT @output = COALESCE(@output + ', ', '') + COL_VCHAR 
from someTable; 
+0

你試過了嗎? – 2012-02-16 18:49:41

+0

@Adrian他們都工作。如果他們工作,問題不是這樣,但如果我說他們都是因爲相同的無證行爲而工作,那麼他們是正確的? – 2012-02-16 18:57:21

回答

2

由於符合SQL的實現可以同時評估所有輸出行,因此也不能保證正常工作。他們今天發生工作是當前SQL Server實現的工件。

您的同事錯誤地斷言COALESCE以某種方式更改了處理模型。

I.e.一個一致的實現可以有效的手在結果集中的每一潛在行(具有評價FROMWHERE),以一個單獨的線程中,SELECT子句中需要的任何處理(大概重組的結果,以評估GROUP BYHAVINGORDER BY之前隨後執行)。

有管理這樣的處理過程中獲得的變量沒有標準的要求,所以每個線程可以「看到」的@output相同的初始值(NULL'',取決於構成你正在使用的),執行它自己的更新計算,然後將該結果值分配給@output - 然後@output的最終值可以是任何單獨的行結果 - 或者其他任何事情。

4

我相信他們因爲相同的底層(無證)行爲都工作。我相當有信心,如果您認爲Itzik已經證明方法A失敗,那麼方法B也將以同樣的方式和同樣的原因失敗,儘管您的同事聲稱。我不知道怎樣

DECLARE @output VARCHAR(100); 
SELECT @output = COALESCE(@output + ', ', '') + COL_VCHAR 

是從什麼不同:

DECLARE @output VARCHAR(100) = ''; 
SELECT @output = @output + ', ' + COL_VCHAR 
-- or SELECT @output += ',' + COL_VCHAR 

那麼究竟是什麼神奇的COALESCE介紹? SQL Server不會改變它的計劃,因爲你正在對輸出AFAIK做些什麼。

我一直使用它們進行動態SQL生成,不記得曾經看到它們失敗,我意識到這不是你的問題。不幸的是,除非你知道任何一種方法都失敗,否則沒有辦法證明它。

幾個月前,我寫了一篇關於字符串連接的博客文章。這不是你的問題完全相關,但羅布Farley的提出,可以考慮的另一個不利因素的COALESCE方法的註釋:

http://sqlblog.com/blogs/aaron_bertrand/archive/2011/03/08/t-sql-tuesday-16-this-is-not-the-aggregate-you-re-looking-for.aspx

+0

感謝您的輸入。關注依賴於大規模生產系統中的無證行爲/特徵。一些修補程序在路上,微軟決定在幕後做不同的事情,這不再起作用並打破了一切。 – 2012-02-16 19:07:52

+0

如果您想要連接值的特定順序,兩個*都會失敗。不是這裏的情況,但如果你這樣做,'for xml'是要走的路。 – 2012-02-16 19:09:58

+0

如果您的擔心是向前兼容,那麼我會說這兩種方法都會引入相同的風險等級。考慮到這一點,正如@Mikael爲其他原因所暗示的那樣,「FOR XML」可能是最有前途的方法。我意識到這不是你想聽到的,但比對不起更安全。封裝這種事情是相當容易的 - 你在做這個連接有多少個不同的查詢?如果數量很多,那麼數據模型應該是一個更重要的問題。 – 2012-02-16 19:12:20

0

在您的例子,有一種方式來獲得不同的結果,那就是,如果將NULL值引入數據集。例如:

set nocount on 

declare @test table (value int) 

insert into @test values (10) 
insert into @test values (20) 
insert into @test values (null) 
insert into @test values (40) 
insert into @test values (50) 

DECLARE @output VARCHAR(max); 

-- Approach A 
SET @output = ''; 

SELECT @output = @output + CAST(value AS VARCHAR(10)) + ';' 
FROM @test 

print 'Result from A:' 
print isnull(@output, '{null}') 

-- Approach B 
set @output = '' 
SELECT @output = COALESCE(@output + ', ', '') + cast(value as varchar(10)) 
from @test 

print 'Result from B:' 
print isnull(@output, '{null}') 

set nocount off 

解決方法A將返回null,而B方法將返回40, 50

0

他們的工作方式不同,第一個例子中增加了額外的字符到輸出變量的結束,是的,他們因爲工作同樣的行爲。

如果您的列也是數字或日期,第一個示例將工作。然而,它只會取出列中任何值的前10個字符。第二個例子不會切斷該列。

COALESCE絕對沒有遞歸性質。 COALESCE是一種用替換值替換空值的方法。

編輯:

我要補充一點,我還比較喜歡第二個版本,因爲你沒有得到額外的分號,產量意味着更多的價值也會隨之結束。