使用SQL Server,我如何分割一个字符串,以便我可以访问项目x?

拿一根“你好,约翰·史密斯”的绳子。我如何通过空格分割字符串并访问索引1应该返回“约翰”的项目?


当前回答

declare @strng varchar(max)='hello john smith'
select (
    substring(
        @strng,
        charindex(' ', @strng) + 1,
        (
          (charindex(' ', @strng, charindex(' ', @strng) + 1))
          - charindex(' ',@strng)
        )
    ))

其他回答

你可以在SQL用户定义函数解析带分隔符的字符串中找到有用的解决方案(来自代码项目)。

你可以使用这个简单的逻辑:

Declare @products varchar(200) = '1|20|3|343|44|6|8765'
Declare @individual varchar(20) = null

WHILE LEN(@products) > 0
BEGIN
    IF PATINDEX('%|%', @products) > 0
    BEGIN
        SET @individual = SUBSTRING(@products,
                                    0,
                                    PATINDEX('%|%', @products))
        SELECT @individual

        SET @products = SUBSTRING(@products,
                                  LEN(@individual + '|') + 1,
                                  LEN(@products))
    END
    ELSE
    BEGIN
        SET @individual = @products
        SET @products = NULL
        SELECT @individual
    END
END

这里的大多数解决方案使用while循环或递归cte。我保证,如果你可以使用空格以外的分隔符,基于集合的方法会更好:

CREATE FUNCTION [dbo].[SplitString]
    (
        @List NVARCHAR(MAX),
        @Delim VARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN ( SELECT [Value], idx = RANK() OVER (ORDER BY n) FROM 
          ( 
            SELECT n = Number, 
              [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
              CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
            FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
              FROM sys.all_objects) AS x
              WHERE Number <= LEN(@List)
              AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
          ) AS y
        );

示例用法:

SELECT Value FROM dbo.SplitString('foo,bar,blat,foo,splunge',',')
  WHERE idx = 3;

结果:

----
blat

您还可以将需要的idx作为参数添加到函数中,但我将把它作为练习留给读者。

您不能仅使用SQL Server 2016中添加的本地STRING_SPLIT函数来实现这一点,因为不能保证输出将按照原始列表的顺序呈现。换句话说,如果你传递3,6,1结果可能是这个顺序,但它可能是1,3,6。我已经在这里请求社区的帮助来改进内置功能:

请帮助改进STRING_SPLIT

有了足够的定性反馈,他们可能会考虑做出以下改进:

STRING_SPLIT不是特性完整的

更多关于拆分函数,为什么(和证明)while循环和递归cte不能扩展,以及更好的替代方案,如果拆分字符串来自应用层:

以正确的方式拆分字符串-或者退而求其次的方式 拆分字符串:后续 分割字符串:现在使用更少的T-SQL 比较字符串分割/连接方法 处理一个整数列表:我的方法 拆分整数列表:另一个汇总 更多关于拆分列表的内容:自定义分隔符、防止重复和维护顺序 在SQL Server中删除字符串中的重复项

在SQL Server 2016或更高版本上,你应该看看STRING_SPLIT()和STRING_AGG():

性能惊喜和假设:STRING_SPLIT() STRING_SPLIT()在SQL Server 2016:后续#1 STRING_SPLIT()在SQL Server 2016:后续#2 SQL Server v.Next: STRING_AGG()性能 使用SQL Server的新STRING_AGG和STRING_SPLIT函数解决老问题

这个模式工作得很好,可以进行推广

Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
                          ^^^^^                                   ^^^^^     ^^^^

注意字段,索引和类型。

让一些表具有类似的标识符

sys.message.1234.warning.A45
sys.message.1235.error.O98
....

然后,你就可以写作了

SELECT Source         = q.value('(/n[1])', 'varchar(10)'),
       RecordType     = q.value('(/n[2])', 'varchar(20)'),
       RecordNumber   = q.value('(/n[3])', 'int'),
       Status         = q.value('(/n[4])', 'varchar(5)')
FROM   (
         SELECT   q = Convert(xml,'<n>'+Replace(fieldName,'.','</n><n>')+'</n>')
         FROM     some_TABLE
       ) Q

拆铸所有零件。

在Azure SQL数据库(基于Microsoft SQL Server但不完全相同的东西)中,STRING_SPLIT函数的签名看起来像这样:

STRING_SPLIT ( string , separator [ , enable_ordinal ] )

当enable_ordinal标志设置为1时,结果将包括一个名为ordinal的列,该列由输入字符串中子字符串的基于1的位置组成:

SELECT *
FROM STRING_SPLIT('hello john smith', ' ', 1)

| value | ordinal |
|-------|---------|
| hello | 1       |
| john  | 2       |
| smith | 3       |

这允许我们这样做:

SELECT value
FROM STRING_SPLIT('hello john smith', ' ', 1)
WHERE ordinal = 2

| value |
|-------|
| john  |

如果enable_ordinal不可用,则有一个技巧,即假定输入字符串中的子字符串是惟一的。在这种情况下,CHAR_INDEX可以用来查找子字符串在输入字符串中的位置:

SELECT value, ROW_NUMBER() OVER (ORDER BY CHARINDEX(value, input_str)) AS ord_pos
FROM (VALUES
    ('hello john smith')
) AS x(input_str)
CROSS APPLY STRING_SPLIT(input_str, ' ')

| value | ord_pos |
|-------+---------|
| hello | 1       |
| john  | 2       |
| smith | 3       |

如果你查看下面关于使用SQL分割字符串的SQL教程,你会发现许多函数可以用于在SQL Server上分割给定的字符串

例如,SplitAndReturnNth UDF函数可用于使用分隔符分割文本,并将第n块作为函数的输出返回

select dbo.SplitAndReturnNth('Hello John Smith',' ',2)