【问题标题】:Splitting delimited text containing quoted delimiters in SQL Server在 SQL Server 中拆分包含引号分隔符的分隔文本
【发布时间】:2012-05-23 04:31:34
【问题描述】:

接下来是Passing a varchar full of comma delimited values to a SQL Server IN function

我想拆分一些逗号分隔的文本,但我需要允许嵌入逗号:

  DECLARE @text NVARCHAR(1000) = 'abc,def,"ghi,jkl",mno';

我期待的结果是:

  abc
  def
  ghi,jkl
  mno

这是我用来分割 CSV 文本的函数。

它使用循环,因此如果性能是一个问题,您可以使用此处的建议进行调整:https://stackoverflow.com/a/878964/482595

CREATE FUNCTION uf_Split
( 
    @Text NVARCHAR(MAX), 
    @Delimiter CHAR(1),
    @Quote CHAR(1)
) 
RETURNS @Result TABLE 
( 
    [Index] INT NOT NULL IDENTITY(1, 1), 
    [Value] NVARCHAR(4000) NULL,
    [CharPos] INT
)
AS 
BEGIN 
    DECLARE @start BIGINT; SET @start = 1
    DECLARE @end BIGINT; SET @end = 1

    IF @Text is null
    BEGIN
      RETURN
    END 

    WHILE 1=1 
    BEGIN 
        SET @end = 
            CASE
                WHEN CHARINDEX(@Quote, @Text, @start) = @start THEN CHARINDEX(@Quote + @Delimiter, @Text, @start + 1)
                ELSE CHARINDEX(@Delimiter, @Text, @start)
            END

        IF ISNULL(@end, 0) = 0 
        BEGIN 
            -- Delimiter could not be found in the remainder of the text:
            INSERT @Result([Value], [CharPos]) VALUES(SUBSTRING(@Text, @start, DATALENGTH(@Text)), @start)
            BREAK 
        END 
        ELSE IF (CHARINDEX(@Quote, @Text, @start) = @start) AND (CHARINDEX(@Quote + @Delimiter, @Text, @start + 1) = @end)
        BEGIN
            INSERT @Result([Value], [CharPos]) VALUES(SUBSTRING(@Text, @start + 1, @end - @start - 1), @start)
            SET @start = @end + 2
        END
        ELSE 
        BEGIN
            INSERT @Result([Value], [CharPos]) VALUES(SUBSTRING(@Text, @start, @end - @start), @start)
            SET @start = @end + 1
        END
    END 

    RETURN
END
GO

【问题讨论】:

  • 在大多数语言中这是一个重要的命题。使用正则表达式非常困难。您通常最擅长使用专门设计用于处理拆分 CSV 格式的代码/功能。这可能是可行的;但它可能不会很有趣。

标签: sql sql-server tsql


【解决方案1】:

我在 t-sql 中使用两阶段拆分进行了拍摄。我绝对有兴趣看看其他人如何处理这个问题。如果这些字符串很大,或者您希望处理大型行集,我会考虑其他选项,可能是 BULK INSERT 或 CLR。​​

declare @data nvarchar(1000) = 'abc,def,"ghi,jkl",mno,"yak","yak,123"';


declare @x xml;
select  @x = cast('<d>' + replace(@data, '"', '</d><d>') + '</d>' as xml);

;with c(d,i)
as  (   select  p.n.value('.', 'nvarchar(max)') AS data,
                case
                    when left(p.n.value('.', 'nvarchar(max)'), 1) = ',' then 1
                    when right(p.n.value('.', 'nvarchar(max)'), 1) = ',' then 1 
                    else 0 
                end
        from    @x.nodes('/d') p(n)
    )       
select  d
from    c
where   i = 0 and len(d) > 0
union all
select  p.n.value('.', 'nvarchar(max)')
from    (   select cast('<d>' + replace(d, ',', '</d><d>') + '</d>' as xml)
            from c
            where   i=1
        ) d(x)
cross
apply   d.x.nodes('/d')p(n)
where   len(p.n.value('.', 'nvarchar(max)')) > 0;

【讨论】:

  • 这个解决方案实际上比我的解决方案执行得更好——在 5 秒内拆分 17,000 个项目。
  • 如果您将我使用的 XML 方法换成基于数字表的方法(例如,在您的链接中),您可能会获得更好的性能。
【解决方案2】:

最好的方法是在函数中定义嵌入逗号的特殊情况,当您拆分检查字符串开头的嵌入逗号并删除该子字符串时。

【讨论】:

    【解决方案3】:

    很好的解决方案,感谢分享!我确实做了一项更改以适应我的情况。我们的一些数据嵌入了&amp;amp;,这会导致非法字符错误。为了解决这个问题,我使用了一个 REPLACE 函数将其从 &amp;amp; 更改为 &amp;amp;,以便可以在 XML 中对其进行解析。然后稍后当我需要将其改回时,我将&amp;amp; 的值替换为&amp;amp;。我确信有更好的方法来做到这一点,但肯定解决了我们的问题。以下是您的代码示例以及我的更改。

    DECLARE @data NVARCHAR(MAX) = 'abc,def,"ghi,jkl",mno,"yak","yak,123","zzzz & yyyy"';
    
    SELECT @data = REPLACE(REPLACE(REPLACE(@data, '&', '&amp;'), '<', '&lt;'), '>', '&gt;'); /**** Replace characters (&, <, >) ****/
    
    DECLARE @x XML = CAST('<d>' + REPLACE(@data, '"', '</d><d>') + '</d>' AS XML);
    
    ;WITH c (d,i) AS
        (
            SELECT  p.n.value('.', 'NVARCHAR(MAX)') AS DATA,
                    CASE
                        WHEN LEFT(p.n.value('.', 'NVARCHAR(MAX)'), 1) = ','
                            THEN 1
                        WHEN RIGHT(p.n.value('.', 'NVARCHAR(MAX)'), 1) = ','
                            THEN 1 
                        ELSE 0 
                    END
            FROM @x.nodes('/d') p(n)
        )
    
    SELECT REPLACE(REPLACE(REPLACE([z].[d], '&amp;', '&'), '&lt;', '<'), '&gt;', '>') AS d /**** Restore characters (&, <, >) ****/
    FROM (
            SELECT d
            FROM c
            WHERE i = 0
                AND LEN(d) > 0
    
            UNION ALL
    
            SELECT p.n.value('.', 'NVARCHAR(MAX)')
            FROM (
                    SELECT cast('<d>' + replace(d, ',', '</d><d>') + '</d>' AS XML)
                    FROM c
                    WHERE i = 1
                ) d(x)
            CROSS APPLY d.x.nodes('/d')p(n)
            WHERE LEN(p.n.value('.', 'NVARCHAR(MAX)')) > 0
        ) AS z
    ORDER BY d;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多