我的表有三行:
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
答案 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 |
+----+-------------+-------+