Сведения о вопросе

Математик

16:03, 1st July, 2020

Теги

sql   sql-server   tsql   split    

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

Просмотров: 468   Ответов: 5

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

Возьмите строку "Hello John Smith". Как я могу split строку пробелом и получить доступ к элементу с индексом 1, который должен возвращать "John"?



  Сведения об ответе

прога

18:03, 1st July, 2020

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

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

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

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

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


  Сведения об ответе

FAriza

18:03, 1st July, 2020

Вы можете найти решение в 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


  Сведения об ответе

lool

18:03, 1st July, 2020

Во-первых, создайте функцию (используя 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

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


  Сведения об ответе

repe

18:03, 1st July, 2020

Большинство решений здесь используют циклы while или рекурсивные CTEs. Я обещаю, что основанный на множестве подход будет превосходен:

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)) = @Delim
          ) AS y
        );

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

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


  Сведения об ответе

PHPH

18:03, 1st July, 2020

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

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

    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

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

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

очистка

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться