2016-12-14 20 views
3

我有這樣如何根據一列特殊的文字串的位置來獲得價值

Col1     Col2 
Adam     1 
Barbara, Catherina  1 
Barbara, Catherina  2 
Adam, Catherina   1 
Adam, Catherina   2 
Barbara, Adam, Daniela 1 
Barbara, Adam, Daniela 2 
Barbara, Adam, Daniela 3 

表需要一個select語句填充COL3像圖所示

Col1     Col2 Col 3 
Adam     1 Adam 
Barbara, Catherina  1 Barbara 
Barbara, Catherina  2 Catherina 
Adam, Catherina   1 Adam 
Adam, Catherina   2 Catherina 
Barbara, Adam, Daniela 1 Barbara 
Barbara, Adam, Daniela 2 Adam 
Barbara, Adam, Daniela 3 Daniela 

我嘗試使用SUITTRING & CHARINDEX,但無法解決這個問題。需要一個SQL,我們可以傳遞Col2值並填充Col3。

請幫忙。 謝謝,Vikram

+0

您使用的是哪個版本的SQL Server? – squillman

+3

存儲逗號分隔的字符串,而不是使用字段和關係是一個非常嚴重的設計錯誤。你需要修復這個bug,而不是尋找掩蓋它的方法。您可以使用'STRING_SPLIT'方法作爲快速修復方法,直到您修復根目錄錯誤。 –

+1

什麼是「A,B,C」值?這張桌子的目的是什麼?有多種方法可以修復此設計。選擇正確的取決於你想要對他們做什麼。您可以使用稀疏列,即每個字母有一列,而不會浪費額外空間。您可以使用XML或JSON類型字段來存儲多個值(查詢速度會很慢)。如果「字母」本身就是實體,那麼您應該爲它們使用一個表格,另一個將它們與「數字」聯繫起來 –

回答

3

具有分析/拆分功能和交叉應用。

我應該補充一點,如果你不能使用或不想使用UDF,那麼解析可以很容易地移植到CROSS APPLY中。

Declare @YourTable table (Col1 varchar(25),Col2 int) 
Insert Into @YourTable Values 
('A',  1), 
('B,C', 1), 
('B,C', 2), 
('A,C', 1), 
('A,C', 2), 
('B,A,D', 1), 
('B,A,D', 2), 
('B,A,D', 3) 

Select A.* 
     ,Col3 = B.RetVal 
From @YourTable A 
Cross Apply (Select * from [dbo].[udf-Str-Parse](A.Col1,',') where RetSeq=A.Col2) B 

返回

Col1 Col2 Col3 
A  1  A 
B,C  1  B 
B,C  2  C 
A,C  1  A 
A,C  2  C 
B,A,D 1  B 
B,A,D 2  A 
B,A,D 3  D 

的UDF如果需要

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10)) 
Returns Table 
As 
Return ( 
    Select RetSeq = Row_Number() over (Order By (Select null)) 
      ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
    From (Select x = Cast('<x>'+ Replace(@String,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i) 
); 
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',') 
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ') 

只是爲了好玩,這是NON-UDF版本

Select A.* 
     ,Col3 = B.RetVal 
From @YourTable A 
Cross Apply (
       Select * From( 
        Select RetSeq = Row_Number() over (Order By (Select null)) 
          ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
        From (Select x = Cast('<x>'+ Replace(A.Col1,',','</x><x>')+'</x>' as xml).query('.')) as A 
        Cross Apply x.nodes('x') AS B(i) 
       ) C where RetSeq=A.Col2 
      ) B 
+0

感謝您的快速回復John。但我的帖子之前並不清楚,我只是編輯它。你可以請現在檢查 – Vikram

+0

@Vikram仍然適用。 –

+0

@Vikram編輯沒有改變任何東西,只是內容。你仍然需要一個函數和CROSS APPLY。並且你仍然*不應該*在字段中存儲分隔值 –

-1

您可以創建一個函數,該函數獲取字符串(col1)和文本索引(col2)並返回正確的值。查詢應該是這樣的:

SELECT col1, col2, GetElement(co1, col2) as col3 
FROM table 

GetElement可以使用WHILE循環找到該值。僞代碼:

index = 1 
while index < col2 
charindex of the ',' 
if index == col2 substring to the next ',' and return 
else index = index + 1 and remove the substring from beginning to the charindex of ',' 
+0

感謝您的快速反應owczarek。但我的帖子之前並不清楚,我只是編輯它。你現在可以檢查嗎? – Vikram

+0

我的解決方案應該仍然有效 - 'GetElement'函數查找','字符的索引並獲取子字符串。唯一缺少的是從每個迭代開始到第一個','的子串應該從'col1'的原始值中刪除。 – owczarek

+0

@owczarek你還沒有發佈解決方案。只有建議使用'WHILE'循環。即使運行代碼,這也是在T-SQL中拆分字符串的最慢方法。有很多關於如何拆分字符串的文章和重複問題。 –

1

這一個只適用於SQL 2016和Azure的SQL數據庫(more on STRING_SPLIT

DECLARE @DaTable TABLE (Col1 varchar(32),Col2 int) 
INSERT INTO @DaTable 
VALUES 
('A',  1), 
('B,C', 1), 
('B,C', 2), 
('A,C', 1), 
('A,C', 2), 
('B,A,D', 1), 
('B,A,D', 2), 
('B,A,D', 3) 

SELECT S.* 
     ,Col3 = T.Value 
From @DaTable S 
Cross Apply (SELECT * FROM 
(Select Value, Row_Number() OVER (ORDER BY (SELECT 0)) AS RowNum from STRING_SPLIT(S.Col1,',')) AS ss 
where ss.RowNum = S.Col2) AS T 

注意對Azure的SQL數據庫:

的STRING_SPLIT功能僅在兼容性可用級別130. [...] 請注意,即使在新的Azure SQL數據庫中,兼容級別120也可能是默認級別。

+0

['STRING_SPLIT'](https://msdn.microsoft.com/en-us/library/mt684588.aspx)從SQL Server 2016開始。我提交了一個編輯到您的答案,以更正您的斷言,它將在2012年和。 – SqlZim

相關問題