2015-01-13 24 views
1

我的目标是SQL字符串在','和'|'上分割sql字符串

'element1|value,element2|value2,element3|value3' 

转换成

'<element1>value</element1> 
<element2>value2</element1> 
<element3>value3</element1>' 

我的想法是

declare @test as varchar(max) = 'element1|value1,element2|value2,element3|value3' 

SELECT CHARINDEX(',',@test) 

SELECT SUBSTRING(@test,0,CHARINDEX(',',@test)) 

我遇到的问题是,我不是很familar与SQL,是有一个列表功能或我可以用来将它分成3个块,然后解剖每个块?

+1

是否必须在SQL中完成? SQL不是为分割字符串而设计的。有另一个层可以解析吗? –

+0

@DStanley你不知道我多么希望这可能是其他地方 – DidIReallyWriteThat

+0

是你想要的输出每行输入多行像你的样本? –

回答

3

可以使用XML功能解析字符串:

;WITH cte AS (SELECT testString = 'element1|value,element2|value2,element3|value3' 
       UNION SELECT 'test,1,2,3' 
      ) 
    ,SplitString AS (SELECT testString, 
          CONVERT(XML,'<String><Section>'+ REPLACE(REPLACE(testString ,'|',','),',', '</Section><Section>') + '</Section></String>') AS xmlString 
         FROM cte 
        )  
SELECT xmlString.value('/String[1]/Section[1]','varchar(100)') AS Col1 
     ,xmlString.value('/String[1]/Section[2]','varchar(100)') AS Col2 
     ,xmlString.value('/String[1]/Section[3]','varchar(100)') AS Col3 
     ,xmlString.value('/String[1]/Section[4]','varchar(100)') AS Col4 
FROM SplitString 

在这里,我只是改变了你的|,并做好了所有的分裂一举,但如果它不是全部,甚至对你能做到这一点分两步进行,首先分拆|,然后分入,

你也可以看看PARSENAME()但它仅限于4个部分,或者你可以创建一个PARSE功能,如:

/******************************************************************************************** 
     Create Parse Function 
********************************************************************************************/ 
CREATE FUNCTION dbo.FN_PARSE(@chunk VARCHAR(4000), @delimiter CHAR(1), @index INT) 
RETURNS VARCHAR(1000) 
AS 
BEGIN 
    DECLARE 
     @curIndex INT = 0, 
     @pos INT = 1, 
     @prevPos INT = 0, 
     @result VARCHAR(1000) 
    WHILE @pos > 0 
    BEGIN 
     SET @pos = CHARINDEX(@delimiter, @chunk, @prevPos); 
     IF(@pos > 0) 
     BEGIN -- Characters between position and previous position 
      SET @result = SUBSTRING(@chunk, @prevPos, @[email protected]) 
     END 
     ELSE 
     BEGIN -- Last Delim 
      SET @result = SUBSTRING(@chunk, @prevPos, LEN(@chunk)) 
     END 
     IF(@index = @curIndex) 
     BEGIN 
      RETURN @result 
     END 
     SET @prevPos = @pos + 1 
     SET @curIndex = @curIndex + 1; 
    END 
    RETURN '' -- Else Empty 
END 

然后被简称:

;WITH cte AS (SELECT testString = 'element1|value,element2|value2,element3|value3' 
       UNION SELECT 'test,1,2,3' 
      ) 
SELECT dbo.FN_PARSE(testString ,'|', 0) AS Col1 
     ,dbo.FN_PARSE(testString ,'|', 1) AS Col2 
     ... 
FROM cte 

注意索引该部分在上述函数中以0开始。

我很喜欢目前的XML版本,但没有做太多的比较测试。

+0

我真的很喜欢这个。另外,将SELECT结束更改为一个SELECT xmlString.query('/')将返回为xml文档,这非常棒。我现在正在寻找组合看起来像一个根节点下的名称/值对xml文档 – DidIReallyWriteThat

0

只是有选择:-),如果最终目标是将输入字符串转换为XML,则可以使用正则表达式替换来执行此操作,并且它不会受限于任何数量的元素:

DECLARE @Sample NVARCHAR(500), 
     @ElementBasedXML NVARCHAR(500), 
     @AttributeBasedXML NVARCHAR(500); 

SELECT @Sample = N'element1|value,element2|value2,element3|value3', 
     @ElementBasedXML = N'<$2>$3</$2>' + NCHAR(10), 
     @AttributeBasedXML = N'<row $2="$3" />' + NCHAR(10); 

SELECT SQL#.RegEx_Replace4k(@Sample, N'(([^|,]+)\|([^|,]+)),?', @ElementBasedXML, 
          -1, 1, Null) AS [ElementBased], 
     SQL#.RegEx_Replace4k(@Sample, N'(([^|,]+)\|([^|,]+)),?', @AttributeBasedXML, 
          -1, 1, Null) AS [AttributeBased]; 

返回:

ElementBased 
------------ 
<element1>value</element1> 
<element2>value2</element2> 
<element3>value3</element3> 

AttributeBased 
-------------- 
<row element1="value" /> 
<row element2="value2" /> 
<row element3="value3" /> 

或者,您也可以拆分在CTE内部的逗号的字符串(给由管道符号分隔的键值对的数量不受限制),然后拆分他们的单一的对分隔符:

DECLARE @Sample2 NVARCHAR(500); 

SELECT @Sample = N'element1|value,element2|value2,element3|value3'; 

;WITH kvpairs AS 
(
    SELECT split.SplitVal, 
     CHARINDEX(N'|', split.SplitVal) AS [PipeLocation], 
     LEN(split.SplitVal) AS [PairLength] 
    FROM SQL#.String_Split4k(@Sample2, N',', 1) split 
), pieces AS 
(
    SELECT LEFT(kvpairs.SplitVal, (kvpairs.PipeLocation - 1)) AS [Key], 
     RIGHT(kvpairs.SplitVal, (kvpairs.PairLength - kvpairs.PipeLocation)) 
       AS [Value] 
    FROM kvpairs 
) 
SELECT CONVERT(XML, N'<' + pieces.[Key] + N'>' 
       + pieces.[Value] 
       + N'</' + pieces.[Key] + N'>') 
FROM pieces 
FOR XML PATH(''); 

请注意:

  • 两个例子都利用SQL#库,这是SQLCLR功能和特效集合(这是我写的,但是这里显示的功能在免费版本)。

  • RegEx方法没有问题,因为在针对表的SELECT语句中使用RegEx方法时,将列作为要评估的表达式传递。 Split/CTE方法需要放入函数(内联TVF)中,以便通过CROSS APPLY在查询中使用。

  • 第二个例子,做拆分,不必使用SQLCLR;它可以使用纯粹的T-SQL拆分器,使用内联计数表或XML。

+0

,而我没有任何问题,不幸的SQL#库不可用于我。 – DidIReallyWriteThat

+0

@CalvinSmith没有SQLCLR策略?无论哪种方式,只是想我会提到这种可能性。第二个例子,做拆分,仍然是一个选择,因为您可以使用纯粹的T-SQL拆分器使用内联计数表或XML。所以你仍然不会受到元素数量的限制。选项#2不必使用SQLCLR。 –