使用T-SQL,从字符串返回第n个分隔元素

Gar*_*del 12 t-sql sql-server split sql-server-2008

我需要创建一个函数,它将返回分隔字符串的第n个元素.

对于数据迁移项目,我使用SQL脚本将存储在SQL Server数据库中的JSON审核记录转换为结构化报告.目标是提供脚本使用的sql脚本和sql函数,而无需任何代码.

(这是一个短期修复,将在ASP.NET/MVC应用程序中添加新的审核功能时使用)

可用的表格示例不缺少分隔字符串.我选择了一个Common Table Expression示例http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings

示例:我想从'1,222,2,67,888,1111'返回67

Shn*_*ugo 25

这是缓解67(类型安全!!)的最简单的答案:

SELECT CAST('<x>' + REPLACE('1,222,2,67,888,1111',',','</x><x>') + '</x>' AS XML).value('/x[4]','int')
Run Code Online (Sandbox Code Playgroud)

这个问题不是关于字符串拆分方法,而是关于如何获取第n个元素.这个IMO是最容易,完全可行的方式:

这是一个真正的单行程序,可以通过空格分隔第2部分:

DECLARE @input NVARCHAR(100)=N'part1 part2 part3';
SELECT CAST(N'<x>' + REPLACE(@input,N' ',N'</x><x>') + N'</x>' AS XML).value('/x[2]','nvarchar(max)')
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用变量作为分隔符和位置(用于sql:variable()直接从查询的值中检索位置):

DECLARE @dlmt NVARCHAR(10)=N' ';
DECLARE @pos INT = 2;
SELECT CAST(N'<x>' + REPLACE(@input,@dlmt,N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)')
Run Code Online (Sandbox Code Playgroud)

如果您的字符串可能包含禁用字符,您仍然可以这样做.sql:column()首先在字符串上使用,隐式替换所有禁用字符和拟合转义序列.

如果 - 另外 - 你的分隔符是分号,这是一个非常特殊的情况.在这种情况下,我首先将分隔符替换为"#DLMT#",并最终将其替换为XML标记:

SET @input=N'Some <, > and &;Other äöü@€;One more';
SET @dlmt=N';';
SELECT CAST(N'<x>' + REPLACE((SELECT REPLACE(@input,@dlmt,'#DLMT#') AS [*] FOR XML PATH('')),N'#DLMT#',N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)');
Run Code Online (Sandbox Code Playgroud)


use*_*983 8

在 Azure SQL 数据库和 SQL Server 2022 上,STRING_SPLIT现在有一个可选的序数参数。如果省略或0传递参数,则该函数将像以前一样运行,仅返回一value列,并且不保证顺序。如果传递带有值的参数1,则该函数将返回 2 列,value并且ordinal(不出所料)它提供了该值在字符串中的序号位置。

因此,如果您想要字符串中的第四个分隔值,'1,222,2,67,888,1111'您可以执行以下操作:

SELECT [value]
FROM STRING_SPLIT('1,222,2,67,888,1111',',',1)
WHERE ordinal = 4;
Run Code Online (Sandbox Code Playgroud)

如果该值在一列中,它将如下所示:

SELECT SS.[value]
FROM dbo.YourTable YT
     CROSS APPLY STRING_SPLIT(YT.YourColumn,',',1) SS
WHERE SS.ordinal = 4;
Run Code Online (Sandbox Code Playgroud)


Dav*_*ton 6

怎么样:

CREATE FUNCTION dbo.NTH_ELEMENT (@Input NVARCHAR(MAX), @Delim CHAR = '-', @N INT = 0)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN (SELECT VALUE FROM STRING_SPLIT(@Input, @Delim) ORDER BY (SELECT NULL) OFFSET @N ROWS FETCH NEXT 1 ROW ONLY)
END
Run Code Online (Sandbox Code Playgroud)

  • STRING_SPLIT()不保证以给定的顺序返回元素。因为这需要v2016,所以最好使用`OPENJSON`,它返回一个[[key]`,它在JSON数组中包含元素的索引。您可能[阅读本](/sf/answers/3598088931/) (2认同)
  • 太糟糕了,您不能对STRING_SPLIT()的作者投反对票-没有排序?几乎完全杀死了它。它应该已经返回了idx,value,所以您可以对索引进行排序...(或哇:从string_Split(str,delim)中选择值,其中idx = @ idx)并实现人们在几秒钟内想要的东西。如果他们在实施之前花了几分钟时间进行了设计。惊人。我本来想使用此功能,但现在我想不起来使用它了,除非您碰巧遇到了一些无序的事情,这很罕见。 (2认同)
  • 因此,使用@Schnugo 的建议,我提出的解决方案变为`CREATE OR ALTER FUNCTION dbo.NTH_ELEMENT (@Input NVARCHAR(MAX), @Delim CHAR = '-', @N INT = 0) RETURNS NVARCHAR(MAX) AS BEGIN RETURN ( SELECT value FROM OPENJSON('["' + REPLACE(@Input, @Delim, '","') + '"]') WHERE [key] = @N) END` (2认同)

Gar*_*del 5

这是我最初的解决方案......它基于Aaron Bertrand的工作http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings

我只是更改了返回类型以使其成为标量函数.

示例:SELECT dbo.GetSplitString_CTE('1,222,2,67,888,1111',',',4)

CREATE FUNCTION dbo.GetSplitString_CTE
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255),
   @ElementNumber int
)
RETURNS VARCHAR(4000)
AS
BEGIN

   DECLARE @result varchar(4000)    
   DECLARE @Items TABLE ( position int IDENTITY PRIMARY KEY,
                          Item VARCHAR(4000)
                         )  

   DECLARE @ll INT = LEN(@List) + 1, @ld INT = LEN(@Delimiter);  

   WITH a AS
   (
       SELECT
           [start] = 1,
           [end]   = COALESCE(NULLIF(CHARINDEX(@Delimiter, 
                       @List, @ld), 0), @ll),
           [value] = SUBSTRING(@List, 1, 
                     COALESCE(NULLIF(CHARINDEX(@Delimiter, 
                       @List, @ld), 0), @ll) - 1)
       UNION ALL
       SELECT
           [start] = CONVERT(INT, [end]) + @ld,
           [end]   = COALESCE(NULLIF(CHARINDEX(@Delimiter, 
                       @List, [end] + @ld), 0), @ll),
           [value] = SUBSTRING(@List, [end] + @ld, 
                     COALESCE(NULLIF(CHARINDEX(@Delimiter, 
                       @List, [end] + @ld), 0), @ll)-[end]-@ld)
       FROM a
       WHERE [end] < @ll
   )
   INSERT @Items SELECT [value]
   FROM a
   WHERE LEN([value]) > 0
   OPTION (MAXRECURSION 0);

   SELECT @result=Item
   FROM @Items
   WHERE position=@ElementNumber

   RETURN @result;
END
GO
Run Code Online (Sandbox Code Playgroud)

  • 首先使用繁重的递归CTE来分割你的字符串,只是挑选出*第n个元素*.这可以更轻松地完成...... (6认同)