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

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

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

5.08.2008 18:15:47
Jarrod Dixon♦ 8.03.2010 19:44:58
встроенный в SQL Server 2016 msdn.microsoft.com/en-us/library/mt684588.aspx
Tim Abell 24.06.2016 10:02:53
Самые высокие ответы здесь - по крайней мере для меня - довольно старомодны и довольно устарели. Процедурный локатор, циклы, рекурсии, CLR, функции, много строк кода ... Может быть интересно прочитать «активные» ответы, чтобы найти более современные подходы.
Shnugo 12.07.2016 10:18:27
Я добавил новый ответ с более современным подходом: stackoverflow.com/a/49669994/632604
Gorgi Rankovski 5.04.2018 10:25:51
Попробуйте получить n-й элемент списка -> portosql.wordpress.com/2019/05/27/enesimo-elemento-lista
José Diz 3.09.2019 20:45:39
30 ОТВЕТОВ
РЕШЕНИЕ

Вы можете найти решение в пользовательской функции 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
192
2.12.2016 02:42:41
почему SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))и нет SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
Beth 29.09.2010 15:13:55
@GateKiller Это решение не поддерживает Unicode, и оно использует жестко закодированное число (18,3), что не делает его жизнеспособной «повторно используемой» функцией.
Filip De Vos 18.03.2011 13:55:40
Это работает, но выделяет много памяти и тратит процессор.
jjxtra 26.05.2015 16:56:29
Начиная с SQL Server 2016, теперь есть встроенная функция, STRING_SPLITкоторая будет разбивать строку и возвращать результат таблицы с одним столбцом, который можно использовать в SELECTвыражении или в другом месте.
qJake 3.04.2017 20:24:41
Жаль, что парни, на которых я работаю, не в 2016 году. Но я буду помнить об этом на случай, если они когда-нибудь получат преимущество. Отличное решение в промежуточный период. Я реализовал это как функцию и добавил разделитель в качестве аргумента.
Brandon Griffin 2.05.2017 19:38:24

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

-- Create temporary table to parse the list of accounting cycles.
DECLARE @tblAccountingCycles table
(
    AccountingCycle varchar(10)
)

DECLARE @vchAccountingCycle varchar(10)
DECLARE @intPosition int

SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ','
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)

IF REPLACE(@vchAccountingCycleIDs, ',', '') <> ''
BEGIN
    WHILE @intPosition > 0
    BEGIN
        SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1)))
        IF @vchAccountingCycle <> ''
        BEGIN
            INSERT INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle)
        END
        SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition)
        SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
    END
END

Концепция почти такая же. Еще одна альтернатива - использовать .NET-совместимость в самом SQL Server 2005. По сути, вы можете написать себе простой метод в .NET, который разделит строку и затем представит ее как хранимую процедуру / функцию.

-1
22.10.2011 16:07:04
пример этого в .NET (процедуры / функции CLR) можно найти здесь cstruter.com/blog/260
cstruter 28.01.2011 05:47:01

Вот UDF, который сделает это. Он вернет таблицу значений с разделителями, не пробовал все сценарии, но ваш пример работает нормально.


CREATE FUNCTION SplitString 
(
    -- Add the parameters for the function here
    @myString varchar(500),
    @deliminator varchar(10)
)
RETURNS 
@ReturnTable TABLE 
(
    -- Add the column definitions for the TABLE variable here
    [id] [int] IDENTITY(1,1) NOT NULL,
    [part] [varchar](50) NULL
)
AS
BEGIN
        Declare @iSpaces int
        Declare @part varchar(50)

        --initialize spaces
        Select @iSpaces = charindex(@deliminator,@myString,0)
        While @iSpaces > 0

        Begin
            Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))

            Insert Into @ReturnTable(part)
            Select @part

    Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))


            Select @iSpaces = charindex(@deliminator,@myString,0)
        end

        If len(@myString) > 0
            Insert Into @ReturnTable
            Select @myString

    RETURN 
END
GO

Вы бы назвали это так:


Select * From SplitString('Hello John Smith',' ')

Изменить: Обновленное решение для обработки разделителей с len> 1, как в:


select * From SplitString('Hello**John**Smith','**')
22
5.08.2008 18:51:07
Не работает для select * из части идентификатора dbo.ethos_SplitString_fn ('guy, wicks, was here', ',') ----------- ------------ -------------------------------------- 1 парень 2 фитиля
Guy 20.10.2008 15:25:24
Остерегайтесь с len (), поскольку он не будет возвращать правильное число, если его аргумент имеет завершающие пробелы. Например, len ('-') = 2.
Rory 17.10.2009 16:30:54
Не работает с: select * from dbo.SplitString ('foo, foo test ,,,, foo', ',')
cbp 14.04.2010 05:14:35
Исправление для cbp. Выберите @myString = substring (@ mystring, @ iSpaces + len (@deliminator), len (@myString) - charindex (@ deliminator, @ myString, 0))
Alxwest 21.05.2012 10:12:11

Попробуй это:

CREATE function [SplitWordList]
(
 @list varchar(8000)
)
returns @t table 
(
 Word varchar(50) not null,
 Position int identity(1,1) not null
)
as begin
  declare 
    @pos int,
    @lpos int,
    @item varchar(100),
    @ignore varchar(100),
    @dl int,
    @a1 int,
    @a2 int,
    @z1 int,
    @z2 int,
    @n1 int,
    @n2 int,
    @c varchar(1),
    @a smallint
  select 
    @a1 = ascii('a'),
    @a2 = ascii('A'),
    @z1 = ascii('z'),
    @z2 = ascii('Z'),
    @n1 = ascii('0'),
    @n2 = ascii('9')
  set @ignore = '''"'
  set @pos = 1
  set @dl = datalength(@list)
  set @lpos = 1
  set @item = ''
  while (@pos <= @dl) begin
    set @c = substring(@list, @pos, 1)
    if (@ignore not like '%' + @c + '%') begin
      set @a = ascii(@c)
      if ((@a >= @a1) and (@a <= @z1))  
        or ((@a >= @a2) and (@a <= @z2))
        or ((@a >= @n1) and (@a <= @n2))
      begin
        set @item = @item + @c
      end else if (@item > '') begin
        insert into @t values (@item)
        set @item = ''
      end
    end 
    set @pos = @pos + 1
  end
  if (@item > '') begin
    insert into @t values (@item)
  end
  return
end

Проверьте это так:

select * from SplitWordList('Hello John Smith')
5
5.08.2008 18:41:34
Я прошел через это, и это совершенно как то, что я хочу! даже я могу также настроить его для игнорирования специальных символов, которые я выбираю!
Vikas 15.09.2010 06:57:48

Я не верю, что в SQL Server есть встроенная функция разбиения, поэтому, кроме UDF, единственный другой ответ, который я знаю, это взлом функции PARSENAME:

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2) 

PARSENAME берет строку и разбивает ее на символ точки. Он принимает число в качестве второго аргумента, и это число указывает, какой сегмент строки возвращать (работает сзади-вперед).

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3)  --return Hello

Очевидная проблема - когда строка уже содержит точку. Я все еще думаю, что использование UDF - лучший способ ... какие-нибудь другие предложения?

355
2.11.2015 10:05:50
Спасибо, Сол ... Я должен отметить, что это решение действительно плохое решение для реальной разработки. PARSENAME ожидает только четыре части, поэтому использование строки из более чем четырех частей приводит к тому, что она возвращает NULL. Решения UDF явно лучше.
Nathan Bedford 1.07.2009 15:54:58
Это отличный хак, а также заставляет меня плакать, что что-то подобное необходимо для чего-то такого простого в реальных языках.
Factor Mystic 12.07.2010 14:09:43
Чтобы индексы работали «правильно», то есть начиная с 1, я угнал ваш угон с помощью REVERSE: REVERSE (PARSENAME (REPLACE (REVERSE ('Hello John Smith'), '', '.')) , 1)) - Возвращает Hello
NothingsImpossible 14.05.2012 13:57:58
@FactorMystic Первая нормальная форма требует, чтобы вы не помещали несколько значений в одно поле. Это буквально первое правило СУРБД. SPLIT()Функция не входит в комплект , потому что она поощряет плохой дизайн базы данных, и база данных не будет оптимизирована для использования данных , сохраненных в этом формате. РСУБД не обязан помогают разработчикам делать глупости , что он был разработан не для ручки. Правильный ответ всегда будет: «Нормализуйте свою базу данных, как мы говорили вам 40 лет назад». Ни SQL, ни RDBMS не виноваты в плохом дизайне.
Bacon Bits 8.12.2014 04:11:25
@BaconBits, хотя я согласен в теории, на практике подобные инструменты полезны при нормализации плохого дизайна, созданного кем-то, кто был до вас.
Tim Abell 24.06.2016 09:34:23

Во-первых, создайте функцию (используя CTE, общее табличное выражение устраняет необходимость во временной таблице)

 create function dbo.SplitString 
    (
        @str nvarchar(4000), 
        @separator char(1)
    )
    returns table
    AS
    return (
        with tokens(p, a, b) AS (
            select 
                1, 
                1, 
                charindex(@separator, @str)
            union all
            select
                p + 1, 
                b + 1, 
                charindex(@separator, @str, b + 1)
            from tokens
            where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
      )
    GO

Затем используйте его как любую таблицу (или измените ее так, чтобы она соответствовала существующему хранимому процессу), например так.

select s 
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1

Обновить

Предыдущая версия не будет работать для входной строки длиннее 4000 символов. Эта версия заботится об ограничении:

create function dbo.SplitString 
(
    @str nvarchar(max), 
    @separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
    select 
        cast(1 as bigint), 
        cast(1 as bigint), 
        charindex(@separator, @str)
    union all
    select
        p + 1, 
        b + 1, 
        charindex(@separator, @str, b + 1)
    from tokens
    where b > 0
)
select
    p-1 ItemIndex,
    substring(
        @str, 
        a, 
        case when b > 0 then b-a ELSE LEN(@str) end) 
    AS s
from tokens
);

GO

Использование остается прежним.

110
7.07.2016 11:07:35
Это элегантно, но работает только для 100 элементов из-за ограничения глубины рекурсии.
Pking 7.11.2012 15:31:18
@Pking, нет, по умолчанию 100(для предотвращения бесконечного цикла). Используйте MAXRECURSION намек , чтобы определить число уровней рекурсии ( 0к 32767, 0«нет предела» - может раздавить сервер). Кстати, гораздо лучший ответ, чем PARSENAME, потому что он универсален :-). +1
Michał Powaga 14.03.2013 14:45:06
При добавлении maxrecursionк этому решению помните этот вопрос и ответы на него. Как настроить maxrecursionопцию для CTE внутри функции с табличным значением .
Michał Powaga 15.03.2013 09:03:46
В частности, обратитесь к ответу Crisfole - его метод несколько замедляет его, но проще, чем большинство других вариантов.
AHiggins 30.07.2015 18:05:52
второстепенный вопрос, но использование не остается прежним, потому что вы изменили имя столбца, поэтому sбольше не определяется
Tim Abell 24.06.2016 09:45:06

Вы можете использовать таблицу чисел для анализа строк.

Создайте таблицу физических чисел:

    create table dbo.Numbers (N int primary key);
    insert into dbo.Numbers
        select top 1000 row_number() over(order by number) from master..spt_values
    go

Создать тестовую таблицу с 1000000 строками

    create table #yak (i int identity(1,1) primary key, array varchar(50))

    insert into #yak(array)
        select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn
    go

Создать функцию

    create function [dbo].[ufn_ParseArray]
        (   @Input      nvarchar(4000), 
            @Delimiter  char(1) = ',',
            @BaseIdent  int
        )
    returns table as
    return  
        (   select  row_number() over (order by n asc) + (@BaseIdent - 1) [i],
                    substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s
            from    dbo.Numbers
            where   n <= convert(int, len(@Input)) and
                    substring(@Delimiter + @Input, n, 1) = @Delimiter
        )
    go

Использование (выводит 3 миллиона строк в 40 с на моем ноутбуке)

    select * 
    from #yak 
    cross apply dbo.ufn_ParseArray(array, ',', 1)

уборка

    drop table dbo.Numbers;
    drop function  [dbo].[ufn_ParseArray]

Производительность здесь не удивительна, но вызов функции из таблицы с миллионами строк - не лучшая идея. При выполнении строки, разбитой на несколько строк, я бы избегал этой функции.

38
28.10.2016 18:06:12
Лучшее решение IMO, остальные имеют какое-то ограничение ... это быстро и может анализировать длинные строки со многими элементами.
Pking 6.12.2012 13:01:09
Почему вы заказываете n по убыванию? Если там, где три элемента, и мы начали нумерацию с 1, то первый элемент будет номером 3, а последний - номером 1. Разве это не даст более интуитивно понятных результатов, если они descбудут удалены?
hatchet - done with SOverflow 28.10.2014 16:13:14
Согласитесь, было бы более интуитивно понятно в направлении asc. Я следовал соглашению parsename (), которое использует desc
Nathan Skerl 28.10.2014 17:43:26
Некоторое объяснение того, как это работает, было бы замечательно
Tim Abell 24.06.2016 09:46:18
В тесте на 100 миллионов строк до 3 полей для разбора ufn_ParseArray не завершился через 25 минут, а REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) из @NothingsImpossible завершился через 1,5 минуты. @hello_earth Как ваше решение будет сравниваться на более длинных строках с более чем 4 полями?
wwmbes 28.10.2016 07:41:32

Я искал решение в сети, и ниже работает для меня. Ref .

И вы вызываете функцию так:

SELECT * FROM dbo.split('ram shyam hari gopal',' ')

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1))       
RETURNS @temptable TABLE (items VARCHAR(8000))       
AS       
BEGIN       
    DECLARE @idx INT       
    DECLARE @slice VARCHAR(8000)        
    SELECT @idx = 1       
    IF len(@String)<1 OR @String IS NULL  RETURN       
    WHILE @idx!= 0       
    BEGIN       
        SET @idx = charindex(@Delimiter,@String)       
        IF @idx!=0       
            SET @slice = LEFT(@String,@idx - 1)       
        ELSE       
            SET @slice = @String       
        IF(len(@slice)>0)  
            INSERT INTO @temptable(Items) VALUES(@slice)       
        SET @String = RIGHT(@String,len(@String) - @idx)       
        IF len(@String) = 0 break       
    END   
    RETURN       
END
6
31.05.2015 10:23:11
Вы не можете легко получить доступ к N-му элементу, используя эту функцию.
Björn Lindqvist 3.10.2014 09:21:53

По моему мнению, вы, ребята, делаете это слишком сложно. Просто создайте CLR UDF и покончите с этим.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

public partial class UserDefinedFunctions {
  [SqlFunction]
  public static SqlString SearchString(string Search) {
    List<string> SearchWords = new List<string>();
    foreach (string s in Search.Split(new char[] { ' ' })) {
      if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) {
        SearchWords.Add(s);
      }
    }

    return new SqlString(string.Join(" OR ", SearchWords.ToArray()));
  }
};
10
19.07.2012 21:46:38
Я думаю, это слишком сложно, потому что мне нужно иметь Visual Studio, затем включить CLR на сервере, затем создать и скомпилировать проект, и, наконец, добавить сборки в базу данных, чтобы использовать его. Но все еще интересный ответ.
Guillermo Gutiérrez 27.09.2012 13:55:53
@ guillegr123, это не должно быть сложным. Вы можете просто скачать и установить (бесплатно!) SQL #, который является библиотекой функций и процедур SQLCLR. Вы можете получить его с SQLsharp.com . Да, я автор, но String_Split включен в бесплатную версию.
Solomon Rutzky 18.08.2013 16:18:55

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

select 
SUBSTRING(column_name,1,CHARINDEX(' ',column_name,1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,1
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)+1
    ,LEN(column_name))
from table_name

SQL FIDDLE

Преимущества:

  • Он разделяет все 3 разделителя подстрок на ''.
  • Нельзя использовать цикл while, так как это снижает производительность.
  • Нет необходимости поворачивать, так как все результирующие подстроки будут отображаться в одной строке

Ограничения:

  • Нужно знать общее нет. пробелов (подстрока).

Примечание : решение может дать подстроку до N.

Чтобы преодолеть ограничение, мы можем использовать следующую ссылку .

Но опять же вышеупомянутое решение не может быть использовано в таблице (на самом деле я не смог его использовать).

Опять же, я надеюсь, что это решение может кому-то помочь.

Обновление: в случае записей> 50000 не рекомендуется использовать, LOOPSпоскольку это приведет к снижению производительности

1
23.05.2017 12:02:53

Здесь я публикую простой способ решения

CREATE FUNCTION [dbo].[split](
          @delimited NVARCHAR(MAX),
          @delimiter NVARCHAR(100)
        ) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
        AS
        BEGIN
          DECLARE @xml XML
          SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'

          INSERT INTO @t(val)
          SELECT  r.value('.','varchar(MAX)') as item
          FROM  @xml.nodes('/t') as records(r)
          RETURN
        END


Выполните функцию следующим образом

  select * from dbo.split('Hello John Smith',' ')
16
3.04.2013 10:08:49
Мне понравилось это решение. Расширено, чтобы вернуть скалярное значение на основе указанного столбца в результатах.
Alan 22.02.2013 22:21:09
Я был сожжен '&' в строке, чтобы разделить, используя это
KeithL 13.11.2018 16:16:30

Как насчет использования stringи values()заявления?

DECLARE @str varchar(max)
SET @str = 'Hello John Smith'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited TABLE(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, '''),(''')
SET @str = 'SELECT * FROM (VALUES(''' + @str + ''')) AS V(A)' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Результат достигнут.

id  item
1   Hello
2   John
3   Smith
10
31.05.2015 10:20:43
Я использовал ваш ответ, но не работал, но я изменил, и это работало с Union All, я использую SQL 2005
angel 13.08.2013 15:06:08

В следующем примере используется рекурсивный CTE

Обновление 18.09.2013

CREATE FUNCTION dbo.SplitStrings_CTE(@List nvarchar(max), @Delimiter nvarchar(1))
RETURNS @returns TABLE (val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level]))
AS
BEGIN
;WITH cte AS
 (
  SELECT SUBSTRING(@List, 0, CHARINDEX(@Delimiter,  @List + @Delimiter)) AS val,
         CAST(STUFF(@List + @Delimiter, 1, CHARINDEX(@Delimiter, @List + @Delimiter), '') AS nvarchar(max)) AS stval, 
         1 AS [level]
  UNION ALL
  SELECT SUBSTRING(stval, 0, CHARINDEX(@Delimiter, stval)),
         CAST(STUFF(stval, 1, CHARINDEX(@Delimiter, stval), '') AS nvarchar(max)),
         [level] + 1
  FROM cte
  WHERE stval != ''
  )
  INSERT @returns
  SELECT REPLACE(val, ' ','' ) AS val, [level]
  FROM cte
  WHERE val > ''
  RETURN
END

Демо на SQLFiddle

5
25.09.2013 06:55:47

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

Если у меня есть строка значений INT с разделителями, и я хочу вернуть таблицу INT (к которой я могу присоединиться). например, 1,20,3343,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
)
-2
19.06.2013 23:42:16

Это то, что я сделал, чтобы получить конкретный токен в строке. (Протестировано в MSSQL 2008)

Сначала создаем следующие функции: (находится в: здесь

CREATE FUNCTION dbo.SplitStrings_Moden
(
   @List NVARCHAR(MAX),
   @Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
  WITH E1(N)        AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
       E2(N)        AS (SELECT 1 FROM E1 a, E1 b),
       E4(N)        AS (SELECT 1 FROM E2 a, E2 b),
       E42(N)       AS (SELECT 1 FROM E4 a, E2 b),
       cteTally(N)  AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) 
                         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
       cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
                         WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
  SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
    FROM cteStart s;

и

create FUNCTION dbo.getToken
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255),
@Pos int
)
RETURNS varchar(max)
as 
begin
declare @returnValue varchar(max);
select @returnValue = tbl.Item from (
select ROW_NUMBER() over (order by (select null)) as id, * from dbo.SplitStrings_Moden(@List, @Delimiter)
) as tbl
where tbl.id = @Pos
return @returnValue
end

тогда вы можете использовать это так:

select dbo.getToken('1111_2222_3333_', '_', 1)

которые возвращают 1111

-1
25.07.2013 11:07:57

Я использую ответ Фредерика, но это не сработало в SQL Server 2005

Я изменил его, и я использую selectс, union allи это работает

DECLARE @str varchar(max)
SET @str = 'Hello John Smith how are you'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''')
SET @str = ' SELECT  ''' + @str + '''  ' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

И набор результатов:

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you
9
31.05.2015 10:26:25
Это действительно здорово, я когда-либо видел в sql вещи, это работало на мою работу, и я ценю это, спасибо!
Abdurrahman I. 24.03.2016 08:36:37
Я очень обрадовался, когда увидел это, потому что это выглядело так чисто и легко для понимания, но, к сожалению, вы не можете поместить это в UDF из-за EXEC. EXECнеявно вызывает хранимую процедуру, и вы не можете использовать хранимые процедуры в UDF.
Kristen Hammack 10.08.2016 13:20:44
Это работает отлично! я искал использование функции (SplitStrings_Moden) отсюда: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments, которая делает это, и это заняло полторы минуты, чтобы разделить данные и вернуть строки, когда используются только 4 номера счета. Я проверил вашу версию с левым соединением на столе с данными о номерах счетов, и это заняло 2 или 3 секунды! Огромная разница и работает без нареканий! Я бы отдал 20 голосов, если это возможно!
MattE 8.03.2019 18:57:00


    Alter Function dbo.fn_Split
    (
    @Expression nvarchar(max),
    @Delimiter  nvarchar(20) = ',',
    @Qualifier  char(1) = Null
    )
    RETURNS @Results TABLE (id int IDENTITY(1,1), value nvarchar(max))
    AS
    BEGIN
       /* USAGE
            Select * From dbo.fn_Split('apple pear grape banana orange honeydew cantalope 3 2 1 4', ' ', Null)
            Select * From dbo.fn_Split('1,abc,"Doe, John",4', ',', '"')
            Select * From dbo.fn_Split('Hello 0,"&""&&&&', ',', '"')
       */

       -- Declare Variables
       DECLARE
          @X     xml,
          @Temp  nvarchar(max),
          @Temp2 nvarchar(max),
          @Start int,
          @End   int

       -- HTML Encode @Expression
       Select @Expression = (Select @Expression For XML Path(''))

       -- Find all occurences of @Delimiter within @Qualifier and replace with |||***|||
       While PATINDEX('%' + @Qualifier + '%', @Expression) > 0 AND Len(IsNull(@Qualifier, '')) > 0
       BEGIN
          Select
             -- Starting character position of @Qualifier
             @Start = PATINDEX('%' + @Qualifier + '%', @Expression),
             -- @Expression starting at the @Start position
             @Temp = SubString(@Expression, @Start + 1, LEN(@Expression)-@Start+1),
             -- Next position of @Qualifier within @Expression
             @End = PATINDEX('%' + @Qualifier + '%', @Temp) - 1,
             -- The part of Expression found between the @Qualifiers
             @Temp2 = Case When @End < 0 Then @Temp Else Left(@Temp, @End) End,
             -- New @Expression
             @Expression = REPLACE(@Expression,
                                   @Qualifier + @Temp2 + Case When @End < 0 Then '' Else @Qualifier End,
                                   Replace(@Temp2, @Delimiter, '|||***|||')
                           )
       END

       -- Replace all occurences of @Delimiter within @Expression with '</fn_Split><fn_Split>'
       -- And convert it to XML so we can select from it
       SET
          @X = Cast('<fn_Split>' +
                    Replace(@Expression, @Delimiter, '</fn_Split><fn_Split>') +
                    '</fn_Split>' as xml)

       -- Insert into our returnable table replacing '|||***|||' back to @Delimiter
       INSERT @Results
       SELECT
          "Value" = LTRIM(RTrim(Replace(C.value('.', 'nvarchar(max)'), '|||***|||', @Delimiter)))
       FROM
          @X.nodes('fn_Split') as X(C)

       -- Return our temp table
       RETURN
    END
2
5.11.2013 00:12:17

Большинство решений здесь используют циклы 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желаемое в качестве аргумента функции, но я оставлю это в качестве упражнения для читателя.

Вы не можете сделать это только с помощью встроенной STRING_SPLITфункции, добавленной в SQL Server 2016, потому что нет гарантии, что выходные данные будут отображаться в порядке исходного списка. Другими словами, если вы передадите 3,6,1результат, скорее всего, будет в таком порядке, но это может быть 1,3,6. Я попросил помощи сообщества в улучшении встроенной функции здесь:

Имея достаточно качественную обратную связь, они могут рассмотреть некоторые из следующих улучшений:

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

На SQL Server 2016 или выше, однако, вы должны посмотреть STRING_SPLIT()и STRING_AGG():

62
8.04.2020 16:52:15
Лучший ответ, ИМХО. В некоторых других ответах есть проблема предела рекурсии SQL 100, но не в этом случае. Очень быстрая и очень простая реализация. Где кнопка +2?
T-moty 21.10.2015 15:01:40
Я попробовал эту функцию дословно с использованием: select * from DBO.SplitString('Hello John smith', ' ');и был получен вывод: Значение Здравствуйте, ello llo lo o Джон Он Хан Хан Смит Miththh h
wwmbes 11.10.2016 10:27:13
@AaronBertrand Первоначальная проблема, опубликованная GateKiller, касается разделителя пробелов.
wwmbes 26.10.2016 12:00:10
@ Майкл Да, это правда. У вас также не будет таблицы для выбора, если у вас нет разрешения ALTER SCHEMA, и вы не сможете выбрать из нее, если у вас нет разрешения SELECT. Вы всегда можете попросить кого-нибудь создать функцию для вас. , Или создать его где-нибудь, где вы можете создать его (даже временно, скажем, в tempdb). А в 2016+ вы должны использовать STRING_SPLIT (), а не функцию, которую вы все равно должны создавать сами.
Aaron Bertrand 27.02.2020 00:19:43
@ReversedEngineer Существует множество применений, которые не заботятся (и не должны заботиться) об исходном порядке списка. «Найти всех клиентов в этом списке». Почему это важно, если список 3,5,2,8или 8,3,5,2? Я думаю, что это довольно неясный крайний случай, когда требовалось бы «Найти всех клиентов в этом списке и отобразить их именно в этом порядке». (Я не спорю с исправлением этого, я просто предполагаю, что мое представление о большинстве случаев использования функции отличается от вашего. Это неподтвержденное доказательство из числа принятых ответов, показывающих функции без порядкового номера.)
Aaron Bertrand 8.04.2020 16:44:32

Рекурсивное решение CTE с серверной болью, протестируйте его

Настройка схемы MS SQL Server 2008 :

create table Course( Courses varchar(100) );
insert into Course values ('Hello John Smith');

Запрос 1 :

with cte as
   ( select 
        left( Courses, charindex( ' ' , Courses) ) as a_l,
        cast( substring( Courses, 
                         charindex( ' ' , Courses) + 1 , 
                         len(Courses ) ) + ' ' 
              as varchar(100) )  as a_r,
        Courses as a,
        0 as n
     from Course t
    union all
      select 
        left(a_r, charindex( ' ' , a_r) ) as a_l,
        substring( a_r, charindex( ' ' , a_r) + 1 , len(a_R ) ) as a_r,
        cte.a,
        cte.n + 1 as n
    from Course t inner join cte 
         on t.Courses = cte.a and len( a_r ) > 0

   )
select a_l, n from cte
--where N = 1

Результаты :

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |
0
16.01.2014 10:38:04

Вот SQL UDF, который может разбить строку и захватить только определенный кусок.

create FUNCTION [dbo].[udf_SplitParseOut]
(
    @List nvarchar(MAX),
    @SplitOn nvarchar(5),
    @GetIndex smallint
)  
returns varchar(1000)
AS  

BEGIN

DECLARE @RtnValue table 
(

    Id int identity(0,1),
    Value nvarchar(MAX)
) 


    DECLARE @result varchar(1000)

    While (Charindex(@SplitOn,@List)>0)
    Begin
        Insert Into @RtnValue (value)
        Select Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
        Set @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
    End

    Insert Into @RtnValue (Value)
    Select Value = ltrim(rtrim(@List))

    select @result = value from @RtnValue where ID = @GetIndex

    Return @result
END
-2
21.04.2014 12:29:28

Простой оптимизированный алгоритм:

ALTER FUNCTION [dbo].[Split]( @Text NVARCHAR(200),@Splitor CHAR(1) )
RETURNS @Result TABLE ( value NVARCHAR(50)) 
AS
BEGIN
    DECLARE @PathInd INT
    Set @Text+=@Splitor
    WHILE LEN(@Text) > 0
    BEGIN
        SET @PathInd=PATINDEX('%'+@Splitor+'%',@Text)
        INSERT INTO  @Result VALUES(SUBSTRING(@Text, 0, @PathInd))
        SET @Text= SUBSTRING(@Text, @PathInd+1, LEN(@Text))
    END
        RETURN 
END
-2
1.05.2014 07:22:47

Я использовал ответ vzczc с использованием рекурсивных cte в течение некоторого времени, но хотел обновить его для обработки разделителя переменной длины, а также для обработки строк с ведущими и запаздывающими «разделителями», например, когда у вас есть файл CSV с записями, такими как :

"Боб", "Смит", "Саннивейл", "CA"

или когда вы имеете дело с шестью частями, как показано ниже. Я широко их использую для регистрации subject_fqn для аудита, обработки ошибок и т. Д., А parsename обрабатывает только четыре части:

[netbios_name].[machine_name].[instance].[database].[schema].[table].[column]

Вот моя обновленная версия, и спасибо vzczc за его оригинальный пост!

select * from [utility].[split_string](N'"this"."string"."gets"."split"."and"."removes"."leading"."and"."trailing"."quotes"', N'"."', N'"', N'"');

select * from [utility].[split_string](N'"this"."string"."gets"."split"."but"."leaves"."leading"."and"."trailing"."quotes"', N'"."', null, null);

select * from [utility].[split_string](N'[netbios_name].[machine_name].[instance].[database].[schema].[table].[column]', N'].[', N'[', N']');

create function [utility].[split_string] ( 
  @input       [nvarchar](max) 
  , @separator [sysname] 
  , @lead      [sysname] 
  , @lag       [sysname]) 
returns @node_list table ( 
  [index]  [int] 
  , [node] [nvarchar](max)) 
  begin 
      declare @separator_length [int]= len(@separator) 
              , @lead_length    [int] = isnull(len(@lead), 0) 
              , @lag_length     [int] = isnull(len(@lag), 0); 
      -- 
      set @input = right(@input, len(@input) - @lead_length); 
      set @input = left(@input, len(@input) - @lag_length); 
      -- 
      with [splitter]([index], [starting_position], [start_location]) 
           as (select cast(@separator_length as [bigint]) 
                      , cast(1 as [bigint]) 
                      , charindex(@separator, @input) 
               union all 
               select [index] + 1 
                      , [start_location] + @separator_length 
                      , charindex(@separator, @input, [start_location] + @separator_length) 
               from   [splitter] 
               where  [start_location] > 0) 
      -- 
      insert into @node_list 
                  ([index],[node]) 
        select [index] - @separator_length                   as [index] 
               , substring(@input, [starting_position], case 
                                                            when [start_location] > 0 
                                                                then 
                                                              [start_location] - [starting_position] 
                                                            else 
                                                              len(@input) 
                                                        end) as [node] 
        from   [splitter]; 
      -- 
      return; 
  end; 
go 
-2
20.08.2014 08:22:56

Почти все другие ответы заменяют разделяемую строку, которая тратит впустую циклы ЦП и выполняет ненужные выделения памяти.

Я расскажу о гораздо лучшем способе разделения строк здесь: http://www.digitalruby.com/split-string-sql-server/

Вот код:

SET NOCOUNT ON

-- You will want to change nvarchar(MAX) to nvarchar(50), varchar(50) or whatever matches exactly with the string column you will be searching against
DECLARE @SplitStringTable TABLE (Value nvarchar(MAX) NOT NULL)
DECLARE @StringToSplit nvarchar(MAX) = 'your|string|to|split|here'
DECLARE @SplitEndPos int
DECLARE @SplitValue nvarchar(MAX)
DECLARE @SplitDelim nvarchar(1) = '|'
DECLARE @SplitStartPos int = 1

SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)

WHILE @SplitEndPos > 0
BEGIN
    SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, (@SplitEndPos - @SplitStartPos))
    INSERT @SplitStringTable (Value) VALUES (@SplitValue)
    SET @SplitStartPos = @SplitEndPos + 1
    SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)
END

SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, 2147483647)
INSERT @SplitStringTable (Value) VALUES(@SplitValue)

SET NOCOUNT OFF

-- You can select or join with the values in @SplitStringTable at this point.
1
13.04.2020 21:53:47

Этот шаблон работает нормально, и вы можете обобщить

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

раскол и отливка всех деталей.

8
11.11.2014 14:31:37
Это единственное решение, которое позволяет приводить к конкретным типам и является умеренно эффективным (CLR по-прежнему наиболее эффективен, но этот подход обрабатывает таблицу строк размером 8 ГБ, 10 токенов и 10 МБ примерно за 9 минут (сервер AWS M3, 4 000 операций ввода-вывода в секунду). подготовленный диск)
Andrew Hill 8.12.2014 03:53:53

Несмотря на то, что аналогично ответу на основе xml от josejuan, я обнаружил, что обработка пути xml только один раз, а затем поворот был умеренно более эффективным:

select ID,
    [3] as PathProvidingID,
    [4] as PathProvider,
    [5] as ComponentProvidingID,
    [6] as ComponentProviding,
    [7] as InputRecievingID,
    [8] as InputRecieving,
    [9] as RowsPassed,
    [10] as InputRecieving2
    from
    (
    select id,message,d.* from sysssislog cross apply       ( 
          SELECT Item = y.i.value('(./text())[1]', 'varchar(200)'),
              row_number() over(order by y.i) as rn
          FROM 
          ( 
             SELECT x = CONVERT(XML, '<i>' + REPLACE(Message, ':', '</i><i>') + '</i>').query('.')
          ) AS a CROSS APPLY x.nodes('i') AS y(i)
       ) d
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as tokens 
    pivot 
    ( max(item) for [rn] in ([3],[4],[5],[6],[7],[8],[9],[10]) 
    ) as data

побежал в 8:30

select id,
tokens.value('(/n[3])', 'varchar(100)')as PathProvidingID,
tokens.value('(/n[4])', 'varchar(100)') as PathProvider,
tokens.value('(/n[5])', 'varchar(100)') as ComponentProvidingID,
tokens.value('(/n[6])', 'varchar(100)') as ComponentProviding,
tokens.value('(/n[7])', 'varchar(100)') as InputRecievingID,
tokens.value('(/n[8])', 'varchar(100)') as InputRecieving,
tokens.value('(/n[9])', 'varchar(100)') as RowsPassed
 from
(
    select id, Convert(xml,'<n>'+Replace(message,'.','</n><n>')+'</n>') tokens
         from sysssislog 
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as data

побежал в 9:20

0
8.12.2014 03:59:24
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

И ИСПОЛЬЗУЙТЕ ЕГО

select *from dbo.fnSplitString('Querying SQL Server','')
0
20.12.2014 11:58:36

Чистое множество на основе решения с использованием 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
1
13.01.2015 06:37:07

если кто-то хочет получить только одну часть разделенного текста, можно использовать это

выберите * fromSplitStringSep ('Word1 wordr2 word3', '')

CREATE function [dbo].[SplitStringSep] 
(
    @str nvarchar(4000), 
    @separator char(1)
)
returns table
AS
return (
    with tokens(p, a, b) AS (
        select 
        1, 
        1, 
        charindex(@separator, @str)
        union all
        select
            p + 1, 
            b + 1, 
            charindex(@separator, @str, b + 1)
        from tokens
        where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
  )
0
31.08.2015 16:37:31

Я разработал это,

declare @x nvarchar(Max) = 'ali.veli.deli.';
declare @item nvarchar(Max);
declare @splitter char='.';

while CHARINDEX(@splitter,@x) != 0
begin
    set @item = LEFT(@x,CHARINDEX(@splitter,@x))
    set @x    = RIGHT(@x,len(@x)-len(@item) )
     select @item as item, @x as x;
end

единственное внимание, которое вам следует уделить, это точка '.' этот конец @x всегда должен быть там.

0
15.10.2015 10:50:09

Вы можете разбить строку в 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); 
2
23.10.2015 10:14:38