【问题标题】:split sql string on ',' and '|'在 ',' 和 '|' 上拆分 sql 字符串
【发布时间】:2015-03-11 17:31:21
【问题描述】:

我的目标是转换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 个块,然后剖析每个块?

【问题讨论】:

  • 必须在 SQL 中完成吗? SQL 不是为拆分字符串而设计的。是否还有其他层可以进行解析?
  • @DStanley 你不知道我多么希望这在其他地方是可能的
  • 您希望的每个输入行的输出多行是否像您的示例一样?
  • @GoatCO 我想要的输出是任何工作对象。一个包含所有信息的字符串,一个有 2 列的表,我可以消费任何东西。

标签: sql xml sql-server-2008 list foreach


【解决方案1】:

您可以使用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版本很感兴趣,但没有做太多的对比测试。

【讨论】:

  • 我真的很喜欢这个。此外,将结尾的 SELECT 更改为仅 SELECT xmlString.query('/') 以 xml 文档的形式返回,这很棒。我现在正在考虑在一个根节点下使用名称/值对组合看起来像 xml 文档
【解决方案2】:

只是有选择 :-),如果最终目标是将输入字符串转换为 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 语句,将列作为要计算的表达式传递。为了通过CROSS APPLY 在查询中使用Split/CTE 方法,需要将其放入一个函数(内联TVF)中。

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

【讨论】:

  • 虽然我对任何一个都没有问题,但不幸的是我无法使用 SQL# 库。
  • @CalvinSmith 没有 SQLCLR 策略?不管怎样,只是想我会提到这种可能性。第二个示例,进行拆分,仍然是一个选项,因为您可以使用纯 T-SQL 拆分器,使用内联计数表或 XML。所以你仍然不会受到元素数量的限制。选项 #2 不必使用 SQLCLR。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-23
  • 2021-08-26
  • 2014-07-01
  • 2021-01-06
  • 2014-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多