将 CSV 字符串中的第 n 个分隔符转换为列

问题描述 投票:0回答:3

我在数据库中有字符串,其中项目的序列号在一起,如下所示

项目表

A_NUMBER 项目
1 i1,i2,i3,i4,i5,i6
2 j1,j2,j3,j4,j5,i6,i7,i8
3 k1,k2,k3

我想要这个字符串的结果输出如下所示:

Srllno1 Srllno2 Srllno3 Srllno4 好好休息
i1 i2 i3 i4 i5,i6
j1 j2 j3 j4 j5,i6,i7,i8
k1 k2 k3

我尝试了这个SQL语句:

SELECT
    A_NUMBER,
    items,
    PARSENAME(REPLACE(items, ',', '.'), 1) AS Srllno1,
    PARSENAME(REPLACE(items, ',', '.'), 2) AS Srllno2,
    PARSENAME(REPLACE(items, ',', '.'), 3) AS Srllno3,
    PARSENAME(REPLACE(items, ',', '.'), 4) AS Srllno4
FROM
    db.itemtable;

但是除非我手动完成,否则我无法获得

SrlRest

我也尝试过

STRING_SPLIT
但没有成功。

CRME:https://dbfiddle.uk/i_N273aI

如何达到想要的效果?

sql sql-server sql-server-2016
3个回答
1
投票

参见示例 STRING_SPLIT

select A_NUMBER,min(items) items
  ,max(case when ordinal=1 then value end)Srllno1
  ,max(case when ordinal=2 then value end)Srllno2
  ,max(case when ordinal=3 then value end)Srllno3
  ,max(case when ordinal=4 then value end)Srllno4
  ,string_agg(case when ordinal>4 then value end,',') within group (order by ordinal)SrllRest
from itemTable
cross apply(select value,ordinal from string_split(items,',',1))i
group by A_NUMBER

输出为

A_NUMBER 项目 Srllno1 Srllno2 Srllno3 Srllno4 好好休息
1 i1,i2,i3,i4,i5,i6 i1 i2 i3 i4 i5,i6
2 j1,j2,j3,j4,j5,i6,i7,i8 j1 j2 j3 j4 j5,i6,i7,i8
3 k1,k2,k3 k1 k2 k3

演示

令人惊讶:) 对于 SQL Server 2017 及更高版本

select A_NUMBER,min(items) items
  ,max(case when ordinal=1 then value end)Srllno1
  ,max(case when ordinal=2 then value end)Srllno2
  ,max(case when ordinal=3 then value end)Srllno3
  ,max(case when ordinal=4 then value end)Srllno4
  ,string_agg(case when ordinal>4 then value end,',') within group (order by ordinal)SrllRest
from itemTable
cross apply(select value,row_number()over(order by (select null))ordinal from string_split(items,','))i
group by A_NUMBER

ordinal
替换为
row_number()over(order by (select null)

对于 SQL Server 2016

select A_NUMBER,items
  ,max(case when ordinal=1 then value end)Srllno1
  ,max(case when ordinal=2 then value end)Srllno2
  ,max(case when ordinal=3 then value end)Srllno3
  ,max(case when ordinal=4 then value end)Srllno4
  ,COALESCE(STUFF(
   (select ','+value from(select value,row_number()over(order by (select null))ordinal 
                            from string_split(items,','))i
    where ordinal>4
     for XML PATH('')
    ), 1, 2, N''
  ), N'')SrllRest
from itemTable
cross apply(select value,row_number()over(order by (select null))ordinal from string_split(items,','))i
group by A_NUMBER,items

string_agg
替换为
select ... XML Path

演示

SQL Server 2008 版本

with r as(
  select 1 lvl, A_NUMBER,items
    ,case when charindex(',',items)>0 then
        substring(items,1,charindex(',',items)-1)
        when len(items)>0 then items
     else null
     end SrllNo1
    ,cast(null as varchar) SrllNo2,cast(null as varchar) SrllNo3,cast(null as varchar) SrllNo4
    ,case when charindex(',',items)>0 then
        substring(items,charindex(',',items)+1,1000)
     else ''
     end SrllRest
  from itemTable
  union all
  select lvl+1 lvl, A_NUMBER,items
    ,SrllNo1
    ,cast(case when lvl=1 and  charindex(',',SrllRest)>0 then
             substring(SrllRest,1,charindex(',',SrllRest)-1)
           when lvl=1 and  len(SrllRest)>0 then SrllRest
     else SrllNo2
     end as varchar) SrllNo2
    ,cast(case when lvl=2 and  charindex(',',SrllRest)>0 then
             substring(SrllRest,1,charindex(',',SrllRest)-1)
           when lvl=2 and  len(SrllRest)>0 then SrllRest
     else SrllNo3
     end as varchar) SrllNo3
    ,cast(case when lvl=3 and  charindex(',',SrllRest)>0 then
             substring(SrllRest,1,charindex(',',SrllRest)-1)
           when lvl=3 and  len(SrllRest)>0 then SrllRest
     else SrllNo4
     end as varchar) SrllNo4
    ,case when charindex(',',SrllRest)>0 then
        substring(SrllRest,charindex(',',SrllRest)+1,1000)
     else ''
     end SrllRest
  from r where len(SrllRest)>0 and lvl<4
)
select * from r
where lvl=4 or len(SrllRest)=0
order by a_number

演示

lvl A_NUMBER 项目 小号1 小号2 小号3 小号4 好好休息
4 1 i1,i2,i3,i4,i5,i6 i1 i2 i3 i4 i5,i6
4 2 j1,j2,j3,j4,j5,i6,i7,i8 j1 j2 j3 j4 j5,i6,i7,i8
3 3 k1,k2,k3 k1 k2 k3
2 4 k1,k2 k1 k2
1 5 k1 k1
4 6 k1,k2,k3,k4 k1 k2 k3 k4
1 7

1
投票

只是为了扩展我的评论。

concat()
,将 null 转换为空字符串是可选的

示例

Declare @YourTable Table ([Item] varchar(50))  Insert Into @YourTable Values 
 ('i1,i2,i3,i4,i5,i6')
,('j1,j2,j3,j4,j5,i6,i7,i8')
,('k1,k2,k3')
 
 Select Item
       ,Pos1 = concat('',JSON_VALUE(JS,'$[0]'))
       ,Pos2 = concat('',JSON_VALUE(JS,'$[1]'))
       ,Pos3 = concat('',JSON_VALUE(JS,'$[2]'))
       ,Pos4 = concat('',JSON_VALUE(JS,'$[3]'))
       ,Rest = concat(    JSON_VALUE(JS,'$[4]')
                     ,','+JSON_VALUE(JS,'$[5]')
                     ,','+JSON_VALUE(JS,'$[6]')
                     ,','+JSON_VALUE(JS,'$[7]')
                     ,','+JSON_VALUE(JS,'$[8]')
                     ,','+JSON_VALUE(JS,'$[9]')
                     ,','+JSON_VALUE(JS,'$[10]')
                     )
 From  @YourTable A
 Cross Apply (values ('["'+replace(string_escape([Item],'json'),',','","')+'"]') ) B(JS)

结果


1
投票

请尝试以下基于 SQL Server 的 XML 和 XQuery 功能的解决方案。

XPath 谓词

/root/r[1]...
等发挥了所有作用。

通过

/root/r[position() ge 5]
谓词检索尾随标记。它是动态的,因此无需对该部分进行硬编码。

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, items VARCHAR(200));
INSERT @tbl VALUES
('i1,i2,i3,i4,i5,i6')
,('j1,j2,j3,j4,j5,i6,i7,i8')
,('k1,k2,k3');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = ',';

SELECT t.*
    -- , c
    , c.value('(/root/r[1]/text())[1]', 'VARCHAR(20)') AS Srllno1
    , c.value('(/root/r[2]/text())[1]', 'VARCHAR(20)') AS Srllno2 
    , c.value('(/root/r[3]/text())[1]', 'VARCHAR(20)') AS Srllno3 
    , COALESCE(c.value('(/root/r[4]/text())[1]', 'VARCHAR(20)'), '') AS Srllno4  
    , REPLACE(c.query('data(/root/r[position() ge 5]/text())').value('.', 'VARCHAR(50)')
        ,SPACE(1),',') AS SrllRest        
FROM @tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
    REPLACE(items, @separator, ']]></r><r><![CDATA[') + 
    ']]></r></root>' AS XML)) AS t1(c);

输出

身份证 项目 Srllno1 Srllno2 Srllno3 Srllno4 好好休息
1 i1,i2,i3,i4,i5,i6 i1 i2 i3 i4 i5,i6
2 j1,j2,j3,j4,j5,i6,i7,i8 j1 j2 j3 j4 j5,i6,i7,i8
3 k1,k2,k3 k1 k2 k3
© www.soinside.com 2019 - 2024. All rights reserved.