2011-11-22 34 views
4

我有一個Sql Server 2K8 R2數據庫,其表的列有一個包含以(char 13和char 10)分隔的倍數值的列。使用T-SQL將值拆分爲多行時遇到的問題

我正在構建一個腳本來將數據導入正確的規範化模式。

我的源表包含這樣的事情:

ID | Value 
________________ 
1 | line 1 
      line 2 
________________ 
2 | line 3 
________________ 
3 | line 4 
      line 5 
      line 6 
________________ 

等。

[編輯]僅供參考,編號爲整數,值類型爲nvarchar(3072)[/編輯]

我要的是查詢表輸出中somethnig這樣的:

ID | Value 
________________ 
1 | line 1 
________________ 
1 | line 2 
________________ 
2 | line 3 
________________ 
3 | line 4 
________________ 
3 | line 5 
________________ 
3 | line 6 
________________ 

我已經閱讀了很多關於SO的答案,並且也在網絡上發現了很多,我發現使用master..sptvalues應該是解決方案。特別是,我試圖重現問題Split one column into multiple rows的解決方案。 然而,沒有成功(懷疑有兩個字符導致問題)。

到現在爲止,我寫了這個查詢:

SELECT 
    T.ID, 
    T.Value, 
    RIGHT(LEFT(T.Value,spt.Number-1), 
    CHARINDEX(char(13)+char(10),REVERSE(LEFT(char(13)+char(10)+T.Value,spt.Number-1)))) as Extracted 
FROM 
    master..spt_values spt, 
    ContactsNew T 
WHERE 
    Type = 'P' AND 
    spt.Number BETWEEN 1 AND LEN(T.Value)+1 
    AND 
     (SUBSTRING(T.Value,spt.Number,2) = char(13)+char(10) OR SUBSTRING(T.Value,spt.Number,2) = '') 

這個查詢,不幸在返回:

ID | Value | Extracted 
________________________________ 
1 | line 1 | <blank> 
      line 2 | 
________________________________ 
1 | line 1 | line 2 
      line 2 | 
________________________________ 
2 | line 3 | <blank> 
________________________________ 
3 | line 4 | <blank> 
      line 5 | 
      line 6 | 
________________________________ 
3 | line 4 | line 5 
      line 5 | line 6 
      line 6 | 
________________________________ 
3 | line 4 | line 6 
      line 5 | 
      line 6 | 
________________________________ 

<blank>是一個空字符串,而不是空字符串。

我希望得到一些幫助來調整我的查詢。

[EDIT2]我的源表包含小於200條記錄,和性能不是必需的,所以我靶向簡單的解決方案,而不是一個有效的一個[EDIT2]

[EDIT3 ]源數據庫是隻讀的。我無法添加存儲過程,函數或clr類型。我必須在單個查詢中執行此操作。 [EDIT3]

[Edit4]奇怪的事情...看來,空格也被視爲分隔符。

如果我運行下面的查詢:

SELECT 
    T.ID, 
    replace(T.Value, '#', ' '), 
    replace(RIGHT(
     LEFT(T.Value,spt.Number-1), 
     CHARINDEX(char(13) + char(10),REVERSE(LEFT(char(10) + char(13)+T.Value,spt.Number-0))) 
     ), '#', ' ') 
FROM 
    master..spt_values spt, 
    ( 
     select contactID, 
     replace(Value,' ', '#') Value 
     from ContactsNew where Value is not null 
    ) T 
WHERE 
    Type = 'P' AND 
    spt.Number BETWEEN 1 AND LEN(T.Value)+1 
    AND 
     (SUBSTRING(T.Value,spt.Number,2) = char(13) + char(10) OR SUBSTRING(T.Value,spt.Number,1) = '') 

我得到的回報(但是,仍然有錯誤的價值觀)的正確數量,同時運行該查詢:上空間也

SELECT 
    T.ID, 
    T.Value, 
    RIGHT(
     LEFT(T.Value,spt.Number-1), 
     CHARINDEX(char(13) + char(10),REVERSE(LEFT(char(10) + char(13)+T.Value,spt.Number-0))) 
     ) 
FROM 
    master..spt_values spt, 
    ( 
     select contactID, 
     Value 
     from ContactsNew where Value is not null 
    ) T 
WHERE 
    Type = 'P' AND 
    spt.Number BETWEEN 1 AND LEN(T.Value)+1 
    AND 
     (SUBSTRING(T.Value,spt.Number,2) = char(13) + char(10) OR SUBSTRING(T.Value,spt.Number,1) = '') 

分裂

回答

1

編輯#1:我已刪除原來的答案文本。嘗試下面的查詢。我稍微修改了你的邏輯。如果您對此有任何疑問,請不要猶豫地發表評論。如果您需要另一個分隔符,只需引入另一個嵌套查詢,以便用CHAR(13)+ CHAR(10)替換該分隔符。

SELECT 
* 
FROM 
(
    SELECT 
     T.ID, 
     T.Value, 
     CASE 
      WHEN CHARINDEX(CHAR(13) + CHAR(10), SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1)) > 0 THEN 
       LEFT(
        SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1), 
        CHARINDEX(CHAR(13) + CHAR(10), SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1)) - 1) 
/* added by Steve B. see comments for the reasons */ 
     when len(T.Value) = spt.Number then right(t.Value, spt.number -1) 
/* end of edit */ 
      ELSE 
       SUBSTRING(T.Value, spt.number, LEN(T.Value) - spt.Number + 1) 
     END EXTRACTED 
    FROM 
     master..spt_values spt, 
     ContactsNew T 
    WHERE 
     Type = 'P' AND 
     spt.Number BETWEEN 1 AND LEN(T.Value)+1 
) X 
WHERE 
    EXTRACTED <> '' AND 
    (
     LEFT(X.VALUE, LEN(EXTRACTED)) = EXTRACTED OR 
     X.Value LIKE '%' + CHAR(13) + CHAR(10) + EXTRACTED + CHAR(13) + CHAR(10) + '%' OR 
     X.Value LIKE '%' + CHAR(13) + CHAR(10) + EXTRACTED 
    ) 
+0

不幸的是,我不能將函數添加到源數據庫(生產服務器)。我必須能夠在單個SQL查詢中執行此操作。 –

+0

@SteveB,好的,我修改了我的答案。 –

+0

請注意,如果字符串是列的全長,那麼這將不起作用 - 「spt_values」中只有2048個'type ='P''值(列爲3072) –

0

一個示例查詢,顯示如何針對類似於所述的某些測試數據執行此類操作。

如果您無法在最終語句中聲明變量,您可以查找/替換它們的值,但它使事情變得更簡單。

這樣做的作用是在分割之前用一個字符替換CR+LF
如果您的數據中正在使用'|',請選擇另一個不用作臨時分隔符的單個字符。

declare @crlf nvarchar(2) = char(10) + char(13) 
declare @cDelim nvarchar(1) = N'|' 

-- test data 
declare @t table 
(id int 
,value nvarchar(3072)) 

insert @t 
select 1, 'line1' + @crlf + 'line2' 
union all select 2, 'line3' 
union all select 3, 'line4' + @crlf + 'line5' + @crlf + 'line6' 
-- /test data 



;WITH charCTE 
AS 
( 
     --split the string into a dataset 
     SELECT D.id, D.value, SUBSTRING(D.s,n,CHARINDEX(@cDelim, D.s + @cDelim,n) -n) AS ELEMENT 
     FROM (SELECT id, value, REPLACE(value,@crlf,@cDelim) as s from @t) AS D 
     JOIN (SELECT TOP 3072 ROW_NUMBER() OVER (ORDER BY a.type, a.number, a.name) AS n 
       FROM master.dbo.spt_values a 
       CROSS 
       JOIN master.dbo.spt_values b 
      ) AS numsCte 
     ON n <= LEN(s) 
     AND SUBSTRING(@cDelim + s,n,1) = @cDelim 
) 
SELECT id, ELEMENT 
FROM charCTE 
order by id, element