根据下面提到的场景,有些人可以解释一下如何用逗号分隔的值到SQL Server的新记录中。
我有一张这样的表
Column 1 | Column 2
----------+--------------
abc | 12345
bcd | 13455,45678
sdf | 78934,13345
我希望结果符合以下方式
Column1 | Column2
--------+----------
abc | 12345
bcd | 13455
bcd | 45678
sdf | 78934
sdf | 13345
答案 0 :(得分:2)
从基于理货的字符串拆分功能开始,如Jeff Moden's DelimitedSplit8K
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
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 (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;
GO
现在你的问题变得非常简单......
IF OBJECT_ID('tempdb..#temp', 'U') IS NOT NULL
DROP TABLE #temp;
CREATE TABLE #temp (
Column1 CHAR(3),
Column2 VARCHAR(8000)
);
INSERT #temp (Column1,Column2) VALUES
('abc', '12345'),
('bcd', '13455,45678'),
('sdf', '78934,13345');
-- the actual query...
SELECT
t.Column1,
dsk.Item
FROM
#temp t
CROSS APPLY dbo.DelimitedSplit8K(t.Column2, ',') dsk;
结果...
Column1 Column2
------- --------
abc 12345
bcd 13455
bcd 45678
sdf 78934
sdf 13345
编辑:上面假设Column2可以在CSV字符串中包含任意数量的元素。如果元素的最大数量为2,则可以跳过拆分器功能并使用以下内容...
SELECT
t.Column1,
v.Column2
FROM
#temp t
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', t.Column2, 1), 0)) ) s (Split)
CROSS APPLY ( VALUES (1, LEFT(t.Column2, s.Split - 1)), (2, substring(t.Column2, ISNULL(s.Split, 0) + 1, 8000)) ) v (rn, Column2)
WHERE
v.Column2 IS NOT NULL
ORDER BY
v.rn;
答案 1 :(得分:2)
Jason的回答将是我的第一选择(1 +)
但是,如果您无法使用(或想要)TVF,这是一种在线方法。
示例强>
Select A.[Column 1]
,[Column 2] = B.RetVal
From YourTable A
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B2.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace([Column 2],',','</x><x>')+'</x>' as xml).query('.')) B1
Cross Apply x.nodes('x') AS B2(i)
) B
<强>返回强>
Column 1 Column 2
abc 12345
bcd 13455
bcd 45678
sdf 78934
sdf 13345
答案 2 :(得分:0)
您可以使用如下所示的基于Tally表的分割器:
select
column1,
split_values
from
(
select
t.column1,
SUBSTRING( t.column2, t1.N, ISNULL(NULLIF(CHARINDEX(',',t.column2,t1.N),0)-t1.N,8000)) as split_values
from @t t
join
(
select
t.column2,
1 as N
from @t t
UNION ALL
select
t.column2,
t1.N + 1 as N
from @t t
join
(
select
top 8000
row_number() over(order by (select NULL)) as N
from
sys.objects s1
cross join
sys.objects s2
) t1
on SUBSTRING(t.column2,t1.N,1) = ','
) t1
on t1.column2=t.column2
)a
order by column1
的 See live demo 强>