如何将每行拆分成多行

时间:2017-05-15 14:56:57

标签: sql sql-server

我的表有三行:

ID  MULTI_CODES
1   10-101-102
2   20-201-202
3   30-301-302

如何编写一个语句来分割每一行,考虑分隔代码

ID  SINGLE_CODE    LEVEL
1   10                1
1   101               2
1   102               3

2   20                1
2   201               2
2   202               3

3   30                1
3   301               2
3   302               3

3 个答案:

答案 0 :(得分:2)

如果您使用的是SQL Server 2016,则可以使用内置的string_split函数。

如果不是,您将需要自己的功能。表现非常出色的是Jeff Moden的tally table method。我的改进版本看起来像这样,允许用户指定分隔符,如果需要特定值:

create function dbo.StringSplit
(
    @str nvarchar(4000) = ' '               -- String to split.
    ,@delimiter as nvarchar(1) = ','        -- Delimiting value to split on.
    ,@num as int = null                     -- Which value to return.
)
returns table
as
return
(
                        -- Start tally table with 10 rows.
    with n(n)   as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n))
                        -- Select the same number of rows as characters in isnull(@str,'') as incremental row numbers.
                    -- Cross joins increase exponentially to a max possible 10,000 rows to cover largest isnull(@str,'') length.
        ,t(t)   as (select top (select len(isnull(@str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
                            -- Return the position of every value that follows the specified delimiter.
        ,s(s)   as (select 1 union all select t+1 from t where substring(isnull(@str,''),t,1) = @delimiter)
                            -- Return the start and length of every value, to use in the SUBSTRING function.
                    -- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
        ,l(s,l) as (select s,isnull(nullif(charindex(@delimiter,isnull(@str,''),s),0)-s,4000) from s)
    select rn as ItemNumber
            ,Item
    from(select row_number() over(order by s) as rn
                ,substring(isnull(@str,''),s,l) as item
        from l
        ) a
    where rn = @num       -- Return a specific value where specified,
        or @num is null   -- Or everything where not.
)
go

使用如下:

declare @t table (ID int, MULTI_CODES nvarchar(50));
insert into @t values (1,'10-101-102'),(2,'20-201-202'),(3,'30-301-302');

select t.ID
        ,t.MULTI_CODES
        ,s.Item as SINGLE_CODE
        ,s.ItemNumber as [Level]
from @t t
    outer apply dbo.StringSplit(t.MULTI_CODES,'-',null) s
order by t.ID
        ,s.ItemNumber;

哪个输出:

+----+-------------+-------------+-------+
| ID | MULTI_CODES | SINGLE_CODE | Level |
+----+-------------+-------------+-------+
|  1 | 10-101-102  |          10 |     1 |
|  1 | 10-101-102  |         101 |     2 |
|  1 | 10-101-102  |         102 |     3 |
|  2 | 20-201-202  |          20 |     1 |
|  2 | 20-201-202  |         201 |     2 |
|  2 | 20-201-202  |         202 |     3 |
|  3 | 30-301-302  |          30 |     1 |
|  3 | 30-301-302  |         301 |     2 |
|  3 | 30-301-302  |         302 |     3 |
+----+-------------+-------------+-------+

答案 1 :(得分:2)

IF OBJECT_ID('tempdb..#TEMPtable') IS NOT NULL
Drop table #TEMPtable
;With cte(ID,  MULTI_CODES)
AS
(
select 1,'10-101-102' UNION ALL
select 2,'20-201-202' UNION ALL
select 3,'30-301-302'
)
SELECT * INTO #TEMPtable FROM cte


SELECT ID, Split.a.value('.','Varchar(100)') AS MULTI_CODES,ROW_NUMBER()Over(Partition by ID Order by ID) AS LEVEL
FROM(

SELECT ID, CASt('<M>' + Replace(MULTI_CODES,'-','</M><M>') +'</M>' AS XML)As  MULTI_CODES

FROM #TEMPtable
)AS A
CROSS APPLY
MULTI_CODES.nodes('/M') AS Split(A)

输出

ID  MULTI_CODES LEVEL
----------------------
1   10          1
1   101         2
1   102         3
2   20          1
2   201         2
2   202         3
3   30          1
3   301         2
3   302         3

答案 2 :(得分:0)

你可以使用cross apply to string_split function

select id, Value as Single_code, RowN as [Level] from #yourcodes cross apply
( 
   select RowN= row_number() over (order by (select null)), value 
    from string_split(Multi_codes, '-') 
) a

输出:

+----+-------------+-------+
| id | Single_code | Level |
+----+-------------+-------+
|  1 |          10 |     1 |
|  1 |         101 |     2 |
|  1 |         102 |     3 |
|  2 |          20 |     1 |
|  2 |         201 |     2 |
|  2 |         202 |     3 |
|  3 |          30 |     1 |
|  3 |         301 |     2 |
|  3 |         302 |     3 |
+----+-------------+-------+