2015-10-13 271 views
1

我試圖將SQL中的列拆分爲多個列。將分號分隔的列拆分爲多列

我的數據是這樣的:

Column1 | Column2 | Column3 
ABC  | 123  | User7;User9 
nbm  | qre  | User1;User2;User3 
POI  | kjh  | User1;User4;User5;User9 

我需要的欄3分成4分新列 - 包含第一個「用戶」的每一列。此列中的每個值都用分號分隔。我遇到的問題之一是Column3可以列出任意數量的用戶(全部用分號隔開),所以我不知道需要添加多少「新」列。

最終輸出需要的樣子:

Column1 | Column2 | Column3 | NewColumn1 | NewColumn2 | ETC. 
+0

是否有你需要在同一個表中的用戶的原因?爲什麼不把它們分離到另一個表中,然後創建一個鏈接表來加入它們?這樣你就不用太擔心會有多少用戶...... –

+0

你實際上是想修改包含數據的表結構還是僅僅用於顯示目的? – DeanOC

+0

您遇到問題的原因是因爲您的數據未正確歸一化。您首先必須將該分隔列表拆分爲可用的東西。然後,您將需要一個動態交叉表或數據透視表來生成您所需的動態列。 –

回答

2

除了這個事實,這是不好的設計,這裏是一個解決方案:

就在這個粘貼到一個空的查詢窗口,然後執行。適應您的需求...

declare @tbl TABLE(Column1 VARCHAR(15),Column2 VARCHAR(15),Column3 VARCHAR(150)); 
INSERT INTO @tbl VALUES 
('ABC','123','User7;User9') 
,('nbm','qre','User1;User2;User3') 
,('POI','kjh','User1;User4;User5;User9'); 

WITH Splitted AS 
(
    SELECT Column1,Column2,CAST('<x>'+REPLACE(Column3,';','</x><x>')+'</x>' AS XML) AS Col3Splitted 
    FROM @tbl 
) 
SELECT Column1,Column2,Col3Splitted 
         ,Col3Splitted.value('x[1]','varchar(max)') AS Column4 
         ,Col3Splitted.value('x[2]','varchar(max)') AS Column5 
         ,Col3Splitted.value('x[3]','varchar(max)') AS Column6 
         ,Col3Splitted.value('x[4]','varchar(max)') AS Column7 
         /*Add as many as you need*/ 
FROM Splitted 

在與@SeanLang的討論後,我添加這種動態的方法。它將計算Column3中分號的最高數量,並動態構建上面的語句。

CREATE TABLE #tbl (Column1 VARCHAR(15),Column2 VARCHAR(15),Column3 VARCHAR(150)); 
INSERT INTO #tbl VALUES 
('ABC','123','User7;User9') 
,('nbm','qre','User1;User2;User3') 
,('POI','kjh','User1;User4;User5;User9'); 

DECLARE @sql VARCHAR(MAX)= 
'WITH Splitted AS 
(
    SELECT Column1,Column2,CAST(''<x>''+REPLACE(Column3,'';'',''</x><x>'')+''</x>'' AS XML) AS Col3Splitted 
    FROM #tbl 
) 
SELECT Column1,Column2'; 
DECLARE @counter INT = 0; 
WHILE @counter<=(SELECT MAX(LEN(Column3) - LEN(REPLACE(Column3, ';', ''))) from #tbl) 
BEGIN 
    SET @[email protected]+1; 
    SET @[email protected]+',Col3Splitted.value(''x[' + CAST(@counter AS VARCHAR(10)) + ']'',''varchar(max)'') AS Column' + CAST(@counter+3 AS VARCHAR(10)); 
END 
SET @[email protected]+ ' FROM Splitted;'; 

EXEC (@sql); 

DROP TABLE #tbl; 
+0

這可行,但不會支持未知數量的列要求。爲此,它需要是動態的。 –

+0

@SeanLange,是,否......您可以添加預期的最大列數,如果爲空,它們將返回NULL。設計很糟糕。這會 - 至少 - 回來一個definit結構;-) – Shnugo

+0

同意設計臭雞蛋超過1000歲,但OP的確表明他們不知道有多少列將被退回。在這方面,你的方法不具有動態性。 –

0

這裏是一個100%動態的方法。它將根據它找到的數據生成任意數量的列。這個圍繞SO的主流方法是一個動態支點。我更喜歡動態交叉表,因爲我覺得它的語法不那麼呆板,而且從性能的角度來看它也有一點好處。 :)

這是一篇文章,很好地解釋了這種方法。 http://www.sqlservercentral.com/articles/Crosstab/65048/

另外,我使用的是由Jeff Moden最初編寫並且隨着時間的推移通過社區改進的DelimitedSplit8K函數。你可以閱讀它並在這裏找到它的代碼。 http://www.sqlservercentral.com/articles/Tally+Table/72993/

if OBJECT_ID('tempdb..#Something') is not null 
    drop table #Something; 

create table #something 
(
    Column1 varchar(5) 
    , Column2 varchar(5) 
    , Column3 varchar(50) 
); 

insert #something 
select 'ABC', '123', 'User7;User9' union all 
select 'nbm', 'qre', 'User1;User2;User3' union all 
select 'POI', 'kjh', 'User1;User4;User5;User9'; 

declare @StaticPortion nvarchar(2000) = 'with orderedResults as 
(
    select s.Column1 
     , s.Column2 
     , x.Item 
     , x.ItemNumber 
    from #something s 
    cross apply dbo.DelimitedSplit8K(Column3, '';'') x 
) 

select Column1 
    , Column2 
'; 

declare @DynamicPortion nvarchar(max) = ''; 

    WITH 
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)), 
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows 
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max 
    cteTally(N) AS 
    (
     SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 
    ) 

select @DynamicPortion = @DynamicPortion + ', MAX(Case when ItemNumber = ' + CAST(N as varchar(6)) + ' then Item end) as Subject' + CAST(N as varchar(6)) + CHAR(10) 
from cteTally t 
where t.N <= 
(
    select top 1 COUNT(*) 
    from #something s 
    cross apply dbo.DelimitedSplit8K(Column3, ';') x 
    group by Column1 
    Order by COUNT(*) desc 
); 

declare @FinalStaticPortion nvarchar(2000) = ' from orderedResults group by Column1, Column2 order by Column1, Column2'; 

declare @SqlToExecute nvarchar(max) = @StaticPortion + @DynamicPortion + @FinalStaticPortion; 

select @SqlToExecute; 
--uncomment the following line when you are sure this is what you want to execute. 
--exec sp_executesql @SqlToExecute; 
0

對於MySQl可以使用下面的代碼。這只是一個示例代碼

@num_Column3 := 1 + LENGTH(@Column3) - LENGTH(REPLACE(@Column3, '|', '')) AS num_Column3 
,IF(@num_Column3 > 0, SUBSTRING_INDEX(SUBSTRING_INDEX(@Column3)), '|', 1), '|', -1), '') AS RC_user