将以分号分隔的列拆分为多个列

时间:2015-10-13 20:49:52

标签: sql sql-server sql-server-2008

我正在尝试将SQL中的列拆分为多个列。

我的数据如下:

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

我需要将Column3拆分为4个新列 - 每列包含第一个“User”。此列中的每个值都用分号分隔。我遇到的一个问题是Column3可以列出任意数量的用户(全部用分号分隔),所以我不知道我需要添加多少“新”列。

最终输出需要看起来像:

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

3 个答案:

答案 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 @counter=@counter+1;
    SET @sql=@sql+',Col3Splitted.value(''x[' +  CAST(@counter AS VARCHAR(10)) + ']'',''varchar(max)'') AS Column' + CAST(@counter+3 AS VARCHAR(10));
END
SET @sql=@sql+ ' FROM Splitted;';

EXEC (@sql);  

DROP TABLE #tbl;

答案 1 :(得分: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;

答案 2 :(得分:0)

对于MySQl,可以使用下面的代码。这只是一个示例代码

Traceback (most recent call last): File "gmail.py", line 9, in m.login(user,pwd) 
File "/usr/lib/python2.7/imaplib.py", line 507, in login raise self.error(dat[-1]) imaplib.error: [ALERT] 
Please log in via your web browser: support.google.com/mail/accounts/answer/78754 (Failure)