以下是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
答案 0 :(得分:3)
我在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;
答案 1 :(得分:0)
这样做的最佳方法是在函数中为嵌入式逗号定义特殊情况,当您在字符串的开头拆分嵌入式逗号并删除该子字符串时。
答案 2 :(得分:0)
很棒的解决方案,谢谢分享!我确实做了一个更改以适合我的情况。我们的某些数据已嵌入&
,这会导致非法字符错误。为了解决该问题,我使用了REPLACE函数将其从&
更改为&
,以便可以在XML中进行解析。然后,当我需要将其改回时,将值&
替换为&
。我相信有更好的方法可以解决此问题,但绝对可以解决我们的问题。下面是带有我的更改的代码示例。
DECLARE @data NVARCHAR(MAX) = 'abc,def,"ghi,jkl",mno,"yak","yak,123","zzzz & yyyy"';
SELECT @data = REPLACE(REPLACE(REPLACE(@data, '&', '&'), '<', '<'), '>', '>'); /**** 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], '&', '&'), '<', '<'), '>', '>') 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;