2012-06-06 60 views
2

我有這樣的一個表:SQL服務器:拆分記錄

account | check1   | check2 
1   | 100]200]300  | 101]209]305 
2   | 401]502   | 404]511 
3   | 600    | 601 

我想記錄分成是這樣的:

account | check1  | check2 
1   | 100  | 101 
1   | 200  | 209 
1   | 300  | 305 
2   | 401  | 404 
2   | 502  | 511 
.   |  .  | . 
.   |  .  | . 
.   |  .  | . 

如何做到這一點使用SQL服務器只

謝謝,

+2

http://whathaveyoutried.com/ –

+1

首先看看拆分字符串函數的實現:http://stackoverflow.com/questions/314824這將最有可能在循環內,你需要第二個循環的分裂功能結果。填充臨時表。您可能想要在存儲過程中執行此操作。 –

+0

在一個側面說明,它看起來像你正在努力規範化。您也可以考慮製作另一個名爲「CheckNumber」的列,而不是爲每個支票設置一列,而是爲每個支票使用一行。 –

回答

5

首先,你需要一個拆分函數,可以讓你確定結果中的順序。這是使用一個IDENTITY列

CREATE FUNCTION dbo.SplitStrings 
(
    @List  NVARCHAR(MAX), 
    @Delimiter NVARCHAR(255) 
) 
RETURNS @t TABLE(ID INT IDENTITY(1,1), Item INT) 
AS 
BEGIN 
    INSERT @t(Item) SELECT SUBSTRING(@List, Number, 
     CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number) 
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) 
     FROM sys.all_objects) AS n(Number) 
    WHERE Number <= CONVERT(INT, LEN(@List)) 
     AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter 
    ORDER BY Number OPTION (MAXDOP 1); 

    RETURN; 
END 
GO 

(如果你有一個數字表,你可以用這個來代替子查詢多語句TVF,而這也將讓你與SCHEMABINDING添加到函數的定義,它提供了潛在的性能優勢)

有了功能,這裏是給你所提供的數據和預期的結果示例用法:

DECLARE @x TABLE(account INT, check1 NVARCHAR(1000), check2 NVARCHAR(1000)); 

INSERT @x SELECT 1, '100]200]300','101]209]305' 
UNION ALL SELECT 2, '401]502','404]511' 
UNION ALL SELECT 3, '600','601' 
UNION ALL SELECT 4, '205]104','304]701'; -- I added this sanity check 

SELECT account, check1 = s1.Item, check2 = s2.Item 
FROM @x AS x 
CROSS APPLY dbo.SplitStrings(x.check1, ']') AS s1 
CROSS APPLY dbo.SplitStrings(x.check2, ']') AS s2 
WHERE s1.ID = s2.ID 
ORDER BY account, s1.ID; 

結果:

account check1 check2 
------- ------ ------ 
1  100  101 
1  200  209 
1  300  305 
2  401  404 
2  502  511 
3  600  601 
4  205  304 
4  104  701 

這假設您有某種確認/執行,check1和check2列中的相應值始終具有相同數量的值。它還假定任何check1/check2值不會超過大約7,000個字符(再次,Numbers表可以幫助使這更加靈活)。

編輯

AndriyM的意見後,我想回來,重新訪問這個,主要是爲了提供一個版本,它不使用多語句TVF作品上述功能的。這可以使用Andriy的想法ROW_NUMBER()。

CREATE FUNCTION dbo.SplitStrings 
(
    @List  NVARCHAR(MAX), 
    @Delimiter NVARCHAR(255) 
) 
RETURNS TABLE 
AS 
    RETURN (SELECT Number = ROW_NUMBER() OVER (ORDER BY Number), 
     Item FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(@List, Number, 
     CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number))) 
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) 
     FROM sys.all_objects) AS n(Number) 
    WHERE Number <= CONVERT(INT, LEN(@List)) 
     AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter 
    ) AS y); 
GO 
+0

你的'SplitStrings'事物正在拉動並插入數據,這意味着你可以很容易地將它重寫爲內聯TVF,但你選擇了一個多語句的。你能解釋一下爲什麼? (我的意思是,如果那不僅僅是一個任意的決定)我這樣問,因爲從最近以來我一直聽說內聯TVF一般應該被多語句TVF所偏好(前者自然可能,在這裏,我相信)。 –

+0

@AndriyM你可以顯示一個不使用IDENTITY列的內聯TVF,但可以保證結果的順序嗎?外部查詢需要某種方式來加入100到101,200到209等。我認爲它可以作爲內聯TVF和「工作」完成,但我不認爲你可以保證輸出順序(這就是爲什麼我添加一行中字符串中的數字不是都以升序排列)。我很樂意被證明是錯誤的,但這不僅僅是一個評論。 :-)(例如,如果你有一個更好的答案...) –

+0

我錯過了IDENTITY位,對不起。我想,你可以使用'ROW_NUMBER()OVER(ORDER BY Number)AS ID'來替換外部選擇。但我不知道這是否會更好。無論如何,我已經得到了我的問題的答案,謝謝。 :) –

0

@Aaron貝特朗與空值在第二列 'CHECK2' 這樣的記錄:

Account | Check1  | Check2 

001  | 100]200  | ] 

002  | 300]400  | Null 

003  | 500]600]700 | ]] 

你的函數沒有返回值是這樣的:

Account | Check1  | Check2 

001  | 100   | 

001  | 200   | 

002  | 300   | Null 

002  | 400   | Null 

003  | 500   | 

003  | 600   | 

003  | 700   | 

如何提高你的函數處理最後一個分隔符後的空值或空字符串?