如何将CallTraversalLog
列的值分隔为每行数据。结果集不得包含任何重复项
SeqNum CallId DNI CallTraversalLog
94329 688 29636 CUSTSEG
94329 688 29636 CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;TPINCHANGE
94332 696 29636 CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;TPINCHANGE
94333 699 29636 CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;CRCENROLL
94333 699 29636 CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;CRCENROLL
94333 702 29636 CUSTSEG;CARDMNU;CRC;CRCBISOA;CRCBI
94334 703 29636 CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;CRCENROLL
94334 703 29636 CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;CRCENROLL
94334 706 29636 CUSTSEG;CARDMNU;CRC;CRCBISOA;CRCBI
94336 710 29636 CUSTSEG;PHPM;PBLOGIN;PBMENU
94340 714 29636 CUSTSEG;PHPM;PBLOGIN;PBMENU;PBBI;PBLST3
我现在迷路了,因为我不知道如何做到这一点。
结果应该是:
Code
CUSTSEG
PHPM
PBLOGIN
PBBI
PBLST3
CARDMNU
CRC
CRCACTIVATION
CRCENROLL
CRCBISOA
CRCBI
TPINCHANGE
我试过这个解决方案,但我得到了
Msg 9411,Level 16,State 1,Line 1
XML解析:第1行,第37个字符,预期分号
我试过的命令是:
SELECT DISTINCT
Split.a.value('.', 'VARCHAR(100)') data
FROM
(SELECT
CAST('<M>' + Replace(CallTraversalLog, ';', '</M><M>') + '</M>' AS XML) AS Data
FROM
tblReportData) AS A
CROSS APPLY
Data.nodes ('/M') AS Split(a)
编辑:我想我知道为什么会收到此错误。某些记录仅包含特殊字符&
。如何替换特殊字符以使查询起作用。
答案 0 :(得分:2)
特殊字符很少,无法直接由XML解析。
您需要将它们替换为以节点值转义
& - &
< - <
> - >
" - "
' - '
SELECT DISTINCT Split.a.value('.', 'VARCHAR(100)') data
FROM (SELECT Cast('<M>'
+ Replace(Replace(Replace(CallTraversalLog, ';', ','), '&', '&'), ',', '</M><M>')
+ '</M>' AS XML) AS Data
FROM tblReportData) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
答案 1 :(得分:0)
您可以通过运行以下脚本来分隔值:
DECLARE @val1 varchar(200) = 'CUSTSEG;PHPM;CARDMNU;CRC;CRCACTIVATION;CRCENROLL'
DECLARE @val2 varchar(20) = null
WHILE LEN(@val1) > 0
BEGIN
IF PATINDEX('%;%',@val1) > 0
BEGIN
SET @val2 = SUBSTRING(@val1, 0, PATINDEX('%;%',@val1))
SELECT @val2
SET @val1 = SUBSTRING(@val1, LEN(@val2 + ';') + 1, LEN(@val1))
END
ELSE
BEGIN
SET @val2 = @val1
SET @val1 = NULL
SELECT @val2
END
END
希望这有帮助。
答案 2 :(得分:0)
使用此分裂功能(也处理一条记录)
alter FUNCTION [dbo].[fnSplitString]
(
@string VARCHAR(MAX),
@delimiter CHAR(1)
)
RETURNS @output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
IF CHARINDEX(@delimiter, @string) IS NULL --deal with one record
BEGIN
SET @string=@string+@delimiter
DECLARE @start INT, @end INT
SELECT @start = 1, @end = CHARINDEX(@delimiter, @string)
WHILE @start < LEN(@string) + 1 BEGIN
IF @end = 0
SET @end = LEN(@string) + 1
INSERT INTO @output (splitdata)
VALUES(SUBSTRING(@string, @start, @end - @start))
SET @start = @end + 1
SET @end = CHARINDEX(@delimiter, @string, @start)
END
RETURN
END
然后使用此查询
DECLARE @tbl TABLE (splt varchar(50))
DECLARE cur CURSOR FAST_FORWARD READ_ONLY
FOR SELECT [CallTraversalLog]
FROM [dbo].[Sample_Table] --replace with your table
DECLARE @i varchar(50)
OPEN cur
FETCH NEXT FROM cur INTO @i
WHILE @@FETCH_STATUS=0
BEGIN
INSERT INTO @tbl
SELECT DISTINCT * FROM [fnSplitString] ( @i,';') as splt
FETCH NEXT FROM cur INTO @i
END
CLOSE cur
DEALLOCATE cur
SELECT DISTINCT * FROM @tbl
答案 3 :(得分:0)
我编写了一个测试脚本,该脚本会遍历每一行,并将分隔的段分解为临时@results
表。对于这样一个简单的任务来说似乎有点费解,可以通过c#/ linq中的几行来实现。这就是SQL。
希望代码和评论充分解释它是如何工作的。
-- SETUP TEST ENVIROMENT
declare @csvTest as table
(VAL nvarchar(100))
declare @result as table
(VAL nvarchar(15))
INSERT INTO @csvTest (VAL)
VALUES('CUSTSEG;PHPM;PBLOGIN;PBMENU;PBBI;PBLST3')
INSERT INTO @csvTest (VAL)
VALUES('CUSTSEG;CARDMNU;CRC;CRCBISOA;CRCBI')
INSERT INTO @csvTest (VAL)
VALUES('ONLYONE')
DECLARE @sCsv nvarchar(100)
-- Loop through each row using a cursor
DECLARE CSV_CURSOR cursor FOR
select VAL
from @csvTest
OPEN CSV_CURSOR
FETCH NEXT FROM CSV_CURSOR
INTO @sCsv
WHILE (@@FETCH_STATUS = 0)
Begin
declare @start int
declare @len int
Set @start = 0
DECLARE @segment nvarchar(15)
while 1=1
Begin
set @segment = ''
-- check if there are any delimiters left
if CHARINDEX(';', @sCsv) = 0
Set @segment = @sCsv
else
select @segment = SUBSTRING(@sCsv, 0, CHARINDEX(';', @sCsv))
-- insert the segment if not already inserted
insert into @result (VAL)
select @segment
where not exists (select * from @result where VAL = @segment)
and @segment != ''
-- break if last
if CHARINDEX(';', @sCsv) = 0
break
-- cut out the segment and continue
Set @sCsv = SUBSTRING(@sCsv, @start + len(@segment) + 2, LEN(@sCsv) - len(@segment) - 1)
End
-- get next row
FETCH NEXT FROM CSV_CURSOR
INTO @sCsv
End
CLOSE CSV_CURSOR
DEALLOCATE CSV_CURSOR
select * from @result
答案 4 :(得分:0)