1

这是从将一个充满逗号分隔值的 varchar 传递给 SQL Server IN 函数开始的

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

  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
4

3 回答 3

4

我在 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;
于 2012-05-23T05:59:54.833 回答
0

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

于 2012-05-23T05:21:49.570 回答
0

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

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;
于 2020-04-23T15:47:29.253 回答