2014-12-04 114 views
-3

如何将CallTraversalLog列的值分隔为每行数据。结果集不能包含任何重复项将不同的varchar数据从一列拆分为多个行

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 

我试过这个解决办法,但我得到

消息9411,级别16,状态1,行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

检查这是工作http://sqlfiddle.com/#!3/6fd60/3 – 2014-12-04 05:43:17

+0

http://www.sqlfiddle.com/#!3/D8418/2 – artm 2014-12-04 05:50:13

+0

我觉得你有特殊字符'&, <, >, “, '''在列CallTraversalLog' – 2014-12-04 05:53:07

回答

2

有几个特殊字符不能被XML直接解析。

您需要更换它们在节点逃脱值

& - &amp; 
< - &lt; 
> - &gt; 
" - &quot; 
' - &#39; 

SELECT DISTINCT Split.a.value('.', 'VARCHAR(100)') data 
FROM (SELECT Cast('<M>' 
        + Replace(Replace(Replace(CallTraversalLog, ';', ','), '&', '&amp;'), ',', '</M><M>') 
        + '</M>' AS XML) AS Data 
     FROM tblReportData) AS A 
     CROSS APPLY Data.nodes ('/M') AS Split(a) 
+0

这一个工作。谢谢 – makalshrek 2014-12-04 06:43:16

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 

希望这有助于。

0

使用这种分裂FUNC(也与一个记录处理)

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 @[email protected][email protected] 

    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 

enter image description here

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