在 ',' 和 '|' 上拆分 sql 字符串

split sql string on ',' and '|'

我的目标是转换 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))

我遇到的问题是我对 sql 不是很熟悉,是否有列表功能或我可以用来将其分成 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, @pos-@prevPos)
        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版本,但是没有做过太多对比测试。

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

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

SELECT @Sample = N'element1|value,element2|value2,element3|value3',
       @ElementBasedXML = N'<></>' + NCHAR(10),
       @AttributeBasedXML = N'<row ="" />' + 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];

Returns:

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 方法很好用在针对 table 的 SELECT 语句中,传入一个列作为要计算的表达式。 Split/CTE 方法需要放入函数(内联 TVF)中才能通过 CROSS APPLY.

  • 在查询中使用
  • 第二个例子,进行拆分,不必使用SQLCLR;它可以使用纯 T-SQL 分离器,使用内联计数 table 或 XML.