Как разделить строку, чтобы получить доступ к элементу x? - PullRequest
472 голосов
/ 05 августа 2008

Как использовать SQL Server, как разделить строку, чтобы получить доступ к элементу x?

Возьми строку "Привет, Джон Смит". Как я могу разбить строку по пробелам и получить доступ к элементу с индексом 1, который должен возвращать «Джон»?

Ответы [ 44 ]

2 голосов
/ 23 октября 2015

Вы можете разбить строку в SQL без необходимости использования функции:

DECLARE @bla varchar(MAX)
SET @bla = 'BED40DFC-F468-46DD-8017-00EF2FA3E4A4,64B59FC5-3F4D-4B0E-9A48-01F3D4F220B0,A611A108-97CA-42F3-A2E1-057165339719,E72D95EA-578F-45FC-88E5-075F66FD726C'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'varchar(36)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE(@bla, ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol);

Если вам нужно поддерживать произвольные строки (со специальными символами xml)

DECLARE @bla NVARCHAR(MAX)
SET @bla = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>,Barnes & Noble,abc,def,ghi'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'nvarchar(MAX)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE((SELECT @bla FOR XML PATH('')), ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol); 
1 голос
/ 20 августа 2018

ПРОСТОЕ РЕШЕНИЕ ДЛЯ ПАРСИНГА ПЕРВОГО И ПОСЛЕДНЕГО ИМЕНИ

DECLARE @Name varchar(10) = 'John Smith'

-- Get First Name
SELECT SUBSTRING(@Name, 0, (SELECT CHARINDEX(' ', @Name)))

-- Get Last Name
SELECT SUBSTRING(@Name, (SELECT CHARINDEX(' ', @Name)) + 1, LEN(@Name))

В моем случае (а во многих других это кажется ...) у меня есть список имен и фамилий, разделенных одним пробелом. Это можно использовать непосредственно внутри оператора select для анализа имени и фамилии.

-- i.e. Get First and Last Name from a table of Full Names
SELECT SUBSTRING(FullName, 0, (SELECT CHARINDEX(' ', FullName))) as FirstName,
SUBSTRING(FullName, (SELECT CHARINDEX(' ', FullName)) + 1, LEN(FullName)) as LastName,
From FullNameTable
1 голос
/ 02 января 2018

Современный подход, использующий STRING_SPLIT , требует SQL Server 2016 и выше.

DECLARE @string varchar(100) = 'Hello John Smith'

SELECT
    ROW_NUMBER() OVER (ORDER BY value) AS RowNr,
    value
FROM string_split(@string, ' ')

Результат:

RowNr   value
1       Hello
2       John
3       Smith

Теперь можно получить n-й элемент из номера строки.

1 голос
/ 22 марта 2018

Аарон Бертран ответ велик, но ошибочен. Он точно не обрабатывает пробел как разделитель (как это было в примере из исходного вопроса), так как функция длины удаляет конечные пробелы.

Ниже приведен его код с небольшой настройкой для ограничения пробела:

CREATE FUNCTION [dbo].[SplitString]
(
    @List NVARCHAR(MAX),
    @Delim VARCHAR(255)
)
RETURNS TABLE
AS
    RETURN ( SELECT [Value] FROM 
      ( 
        SELECT 
          [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+'x')-1) = @Delim
      ) AS y
    );
1 голос
/ 27 апреля 2018

Вот функция, которая выполнит поставленную в вопросе задачу - разбить строку и получить доступ к элементу X:

CREATE FUNCTION [dbo].[SplitString]
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255),
   @ElementNumber INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN

       DECLARE @inp VARCHAR(MAX)
       SET @inp = (SELECT REPLACE(@List,@Delimiter,'_DELMTR_') FOR XML PATH(''))

       DECLARE @xml XML
       SET @xml = '<split><el>' + REPLACE(@inp,'_DELMTR_','</el><el>') + '</el></split>'

       DECLARE @ret VARCHAR(MAX)
       SET @ret = (SELECT
              el = split.el.value('.','varchar(max)')
       FROM  @xml.nodes('/split/el[string-length(.)>0][position() = sql:variable("@elementnumber")]') split(el))

       RETURN @ret

END

Использование:

SELECT dbo.SplitString('Hello John Smith', ' ', 2)

Результат:

John
1 голос
/ 05 сентября 2017

Начиная с SQL Server 2016 мы string_split

DECLARE @string varchar(100) = 'Richard, Mike, Mark'

SELECT value FROM string_split(@string, ',')
1 голос
/ 18 сентября 2018

Я знаю, что уже поздно, но недавно я выполнил это требование и предложил следующий код. У меня нет выбора использовать пользовательскую функцию. Надеюсь, это поможет.

SELECT 
    SUBSTRING(
                SUBSTRING('Hello John Smith' ,0,CHARINDEX(' ','Hello John Smith',CHARINDEX(' ','Hello John Smith')+1)
                        ),CHARINDEX(' ','Hello John Smith'),LEN('Hello John Smith')
            )
1 голос
/ 13 января 2015

Чистое основанное на множестве решение, использующее TVF с рекурсивным CTE. Вы можете JOIN и APPLY эту функцию для любого набора данных.

create function [dbo].[SplitStringToResultSet] (@value varchar(max), @separator char(1))
returns table
as return
with r as (
    select value, cast(null as varchar(max)) [x], -1 [no] from (select rtrim(cast(@value as varchar(max))) [value]) as j
    union all
    select right(value, len(value)-case charindex(@separator, value) when 0 then len(value) else charindex(@separator, value) end) [value]
    , left(r.[value], case charindex(@separator, r.value) when 0 then len(r.value) else abs(charindex(@separator, r.[value])-1) end ) [x]
    , [no] + 1 [no]
    from r where value > '')

select ltrim(x) [value], [no] [index] from r where x is not null;
go

Использование:

select *
from [dbo].[SplitStringToResultSet]('Hello John Smith', ' ')
where [index] = 1;

Результат:

value   index
-------------
John    1
0 голосов
/ 20 июня 2013

Вот мое решение, которое может кому-то помочь. Модификация ответа Jonesinator выше.

Если у меня есть строка значений INT с разделителями, и я хочу вернуть таблицу INT (к которой я могу присоединиться). например '1,20,3,343,44,6,8765'

Создать UDF:

IF OBJECT_ID(N'dbo.ufn_GetIntTableFromDelimitedList', N'TF') IS NOT NULL
    DROP FUNCTION dbo.[ufn_GetIntTableFromDelimitedList];
GO

CREATE FUNCTION dbo.[ufn_GetIntTableFromDelimitedList](@String NVARCHAR(MAX),                 @Delimiter CHAR(1))

RETURNS @table TABLE 
(
    Value INT NOT NULL
)
AS 
BEGIN
DECLARE @Pattern NVARCHAR(3)
SET @Pattern = '%' + @Delimiter + '%'
DECLARE @Value NVARCHAR(MAX)

WHILE LEN(@String) > 0
    BEGIN
        IF PATINDEX(@Pattern, @String) > 0
        BEGIN
            SET @Value = SUBSTRING(@String, 0, PATINDEX(@Pattern, @String))
            INSERT INTO @table (Value) VALUES (@Value)

            SET @String = SUBSTRING(@String, LEN(@Value + @Delimiter) + 1, LEN(@String))
        END
        ELSE
        BEGIN
            -- Just the one value.
            INSERT INTO @table (Value) VALUES (@String)
            RETURN
        END
    END

RETURN
END
GO

Тогда получите результаты таблицы:

SELECT * FROM dbo.[ufn_GetIntTableFromDelimitedList]('1,20,3,343,44,6,8765', ',')

1
20
3
343
44
6
8765

И в заявлении соединения:

SELECT [ID], [FirstName]
FROM [User] u
JOIN dbo.[ufn_GetIntTableFromDelimitedList]('1,20,3,343,44,6,8765', ',') t ON u.[ID] = t.[Value]

1    Elvis
20   Karen
3    David
343  Simon
44   Raj
6    Mike
8765 Richard

Если вы хотите вернуть список NVARCHAR вместо INT, просто измените определение таблицы:

RETURNS @table TABLE 
(
    Value NVARCHAR(MAX) NOT NULL
)
0 голосов
/ 31 октября 2016

опираясь на решение @NothingsImpossible, или, точнее, комментируя наиболее проголосованный ответ (чуть ниже принятого), я обнаружил, что следующее быстрое и грязное решение отвечает моим собственным потребностям - оно имеет преимущество только в пределах домена SQL.

дана строка «первый; второй; третий; четвертый; пятый», скажем, я хочу получить третий токен. это работает только в том случае, если мы знаем, сколько токенов будет иметь строка - в данном случае это 5. поэтому мой способ действия - отрубить последние два токена (внутренний запрос), а затем отобрать первые два токена ( внешний запрос)

Я знаю, что это безобразно и охватывает определенные условия, в которых я находился, но я публикую это на тот случай, если кто-то посчитает это полезным. ура

select 
    REVERSE(
        SUBSTRING(
            reverse_substring, 
            0, 
            CHARINDEX(';', reverse_substring)
        )
    ) 
from 
(
    select 
        msg,
        SUBSTRING(
            REVERSE(msg), 
            CHARINDEX(
                ';', 
                REVERSE(msg), 
                CHARINDEX(
                    ';',
                    REVERSE(msg)
                )+1
            )+1,
            1000
        ) reverse_substring
    from 
    (
        select 'first;second;third;fourth;fifth' msg
    ) a
) b
...