2012-03-30 96 views
2

我不知道這個問題是否曾被問過。但是在拼接varchars時遇到問題。按字母順序排列varchar時排序。不按預期方式工作

讓我解釋一下:

我有這個表:

CREATE TABLE Table1 
(
    [Field] [varchar](10) NOT NULL 
) 
INSERT INTO Table1 
VALUES('1'),('2'),('3'),('4'),('5'),('TITLE') 

而且我喜歡的輸出是這樣的:

'[TITLE],[1],[2],[3],[4],[5]' 

我想要的 'TITLE' 是爲了首先1,2,3,4,5

所以這個查詢將返回有序的結果。我沒有更多的數字那麼 'TITLE'

SELECT 
    * 
FROM 
    Table1 
ORDER BY 
    LEN([Field]) DESC, 
    [Field] ASC 

隨後的長度我通常Concat的是這樣的VARCHAR:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM 
    Table1 
ORDER BY 
    LEN([Field]) DESC, 
    [Field] ASC 

但這種回報:

'[5]' 

其中我發現真的很奇怪。有人可以解釋爲什麼嗎?

我知道有一個替代解決方案來concate一個varchar。就像這樣:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols=STUFF 
(
    (
     SELECT 
      ',' +QUOTENAME([Field]) 
     FROM 
      Table1 
     ORDER BY 
      LEN([Field]) DESC, 
      [Field] ASC 
     FOR XML PATH('') 
    ) 
,1,1,'') 

這將返回我的預期的結果是這樣的:

'[TITLE],[1],[2],[3],[4],[5]' 

編輯

建議,@cols是在開始時爲空不能吧。因爲我刪除了order by。就像這樣:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM 
    Table1 

我的結果會是這樣的:

'[1],[2],[3],[4],[5],[TITLE]' 

EDIT1

這是行不通的:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM(
    SELECT [Field] 
    FROM 
     Table1 
    ORDER BY 
     LEN([Field]) DESC, 
     [Field] ASC 
) AS t 

因爲它會給一個異常消息像這樣:

消息1033,級別15,狀態1,行17 ORDER BY子句是 視圖,內聯函數,派生表,子查詢和公用表 表達式無效,除非TOP或FOR XML還指定。

EDIT2

就像我的預期。我不能做到這一點:

SELECT @cols, @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM 
    Table1 
ORDER BY 
    LEN([Field]) DESC, 
    [Field] ASC 

監守這將提高這樣一個例外:

消息141,級別15,狀態1,行9 SELECT語句,一個 值分配給一個變量必須不能與數據檢索 操作相結合。

+0

你能解釋一下這個場景的推理嗎?通常當你在一個文本字段中存儲數字時,你做錯了。此外,你必須絕對肯定,該表不會增長到100.000行。 – SWeko 2012-03-30 07:56:50

+0

我想在一個動態的'Pivot'中做到這一點。該表格是靜態的,除了一個表示它被禁用的標誌。女巫我沒有包括在這個例子中。該表是我無法更改的遺留系統的一部分。這是客戶要求的報告。 – Arion 2012-03-30 07:59:57

+0

爲什麼你不能使用你的第二個解決方案?它按預期工作。 – SWeko 2012-03-30 08:12:00

回答

2

你可以看看這個文章PRB: Execution Plan and Results of Aggregate Concatenation Queries Depend Upon Expression Location

的ANSI SQL-92規範要求由 一個ORDER BY引用子句任何列匹配結果集,由存在於SELECT列表中列 定義。將表達式應用於ORDER BY子句的成員 時,結果列不在 SELECT列表中公開,導致未定義的行爲。

當您在order by子句中使用表達式時,會得到不同的執行計劃。

enter image description here

排序被計算標量,而不是之前之後施加。

串聯字符串的安全方法是使用for xml

+0

謝謝你。這是接受的答案。 – Arion 2012-03-30 08:37:36

3

我不確定,但我認爲答案是在查詢的執行順序。我試過下面的代碼:

DECLARE @x INT 
SET @x = 0 
SELECT @x [email protected] + 1 
FROM 
    Table1 
ORDER BY 
    LEN(Field) DESC, Field ASC 
SELECT @x 

,並返回1,但

DECLARE @x INT 
SET @x = 0 
SELECT @x [email protected] + 1 
FROM 
    Table1 
SELECT @x 

返回6,這充其量是奇數。

然而,從第一個查詢的執行計劃可以看出,事件的順序是:

Table Scan -> Compute Scalar -> Sort -> Select 

所以我最好的猜測是,結果被首先計算,然後排序,你結束了一些中間值。

恕我直言,這是濫用SQL服務器執行引擎,基本上是未定義的行爲。即使你設法在這個SQL服務器上工作,也不知道服務包是否會爲你打破它。我會選擇你所描述的第二種解決方案,這很直接,而且容易維護。


關於EDIT1:指定SELECT TOP 100 PERCENT採取「視圖中沒有順序」的護理差錯,但是,SQL Server做一些魔術,你還是落得[1],[2],[3],[4],[5],[TITLE]

+0

現在這可能是一個愚蠢的問題。但是你可以改變順序嗎,所以'Sortute'之前的'Compute Scalar'首先是優先級?我同意替代解決方案可能會更好。也在維護方面。 – Arion 2012-03-30 08:27:07

+0

總之,沒有。您可以使用表提示來影響某些執行計劃,但通常情況下,執行計劃路徑是內部SQL服務器。 – SWeko 2012-03-30 08:35:08