Вопрос:

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

sql sql-server tsql split

782027 просмотра

30 ответа

33940 Репутация автора

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

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

Автор: GateKiller Источник Размещён: 05.08.2008 06:15

Ответы (30)


181 плюса

3687 Репутация автора

Решение

Вы можете найти решение в SQL User Defined Function для синтаксического анализа разделительной строки (из проекта Code ).

Вы можете использовать эту простую логику:

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
Автор: Jonesinator Размещён: 05.08.2008 06:28

0 плюса

23261 Репутация автора

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

Автор: Dillie-O Размещён: 05.08.2008 06:36

20 плюса

21811 Репутация автора

Вот 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','**')
Автор: brendan Размещён: 05.08.2008 06:39

5 плюса

34691 Репутация автора

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

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')
Автор: Seibar Размещён: 05.08.2008 06:41

344 плюса

5876 Репутация автора

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

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

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

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

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

Автор: Nathan Bedford Размещён: 05.08.2008 06:45

107 плюса

6882 Репутация автора

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

 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

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

Автор: vzczc Размещён: 05.08.2008 06:57

37 плюса

7217 Репутация автора

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

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

    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]

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

Автор: Nathan Skerl Размещён: 27.10.2008 04:48

6 плюса

12257 Репутация автора

Я искал решение в сети, и ниже работает для меня. 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
Автор: kta Размещён: 20.11.2011 06:40

10 плюса

709 Репутация автора

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

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()));
  }
};
Автор: Damon Drake Размещён: 19.07.2012 09:46

2 плюса

9162 Репутация автора

I know it's an old Question, but i think some one can benefit from my solution.

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

Advantages:

  • It separates all the 3 sub-strings deliminator by ' '.
  • One must not use while loop, as it decreases the performance.
  • No need to Pivot as all the resultant sub-string will be displayed in one Row

Limitations:

  • One must know the total no. of spaces (sub-string).

Note: the solution can give sub-string up to to N.

To overcame the limitation we can use the following ref.

But again the above solution can't be use in a table (Actaully i wasn't able to use it).

Again i hope this solution can help some-one.

Обновление: в случае записей> 50000 нецелесообразно использовать, LOOPSпоскольку это ухудшит производительность

Автор: Luv Размещён: 24.01.2013 06:43

15 плюса

385 Репутация автора

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

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',' ')
Автор: Sivaganesh Tamilvendhan Размещён: 30.01.2013 09:41

10 плюса

611 Репутация автора

Как насчет использования 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
Автор: Frederic Размещён: 01.03.2013 04:26

5 плюса

12524 Репутация автора

В следующем примере используется рекурсивный 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

Автор: Aleksandr Fedorenko Размещён: 14.03.2013 10:18

0 плюса

1088 Репутация автора

Это то, что я сделал, чтобы получить конкретный токен в строке. (Протестировано в 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

Автор: Rotem Размещён: 25.07.2013 11:07

8 плюса

1569 Репутация автора

Я использую ответ frederic, но это не работает в 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
Автор: angel Размещён: 13.08.2013 03:11

3 плюса

541 Репутация автора



    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

Автор: T-Rex Размещён: 05.11.2013 12:12

56 плюса

204866 Репутация автора

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

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
        );

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

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

Автор: Aaron Bertrand Размещён: 12.11.2013 05:16

2 плюса

11843 Репутация автора

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

Я рассматриваю гораздо лучший способ сделать разделение строк здесь: 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.
Автор: jjxtra Размещён: 26.08.2014 04:50

8 плюса

5448 Репутация автора

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

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

расщепление и литье всех деталей.

Автор: josejuan Размещён: 11.11.2014 02:31

1 плюс

6293 Репутация автора

Чистое решение на основе набора 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
Автор: Andrey Morozov Размещён: 13.01.2015 06:37

2 плюса

43133 Репутация автора

Вы можете разбить строку в 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); 
Автор: Stefan Steiger Размещён: 23.10.2015 10:07

6 плюса

698 Репутация автора

Еще одна часть n-й части строки по функции деления:

create function GetStringPartByDelimeter (
    @value as nvarchar(max),
    @delimeter as nvarchar(max),
    @position as int
) returns NVARCHAR(MAX) 
AS BEGIN
    declare @startPos as int
    declare @endPos as int
    set @endPos = -1
    while (@position > 0 and @endPos != 0) begin
        set @startPos = @endPos + 1
        set @endPos = charindex(@delimeter, @value, @startPos)

        if(@position = 1) begin
            if(@endPos = 0)
                set @endPos = len(@value) + 1

            return substring(@value, @startPos, @endPos - @startPos)
        end

        set @position = @position - 1
    end

    return null
end

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

select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)

который возвращает:

c
Автор: Ramazan Binarbasi Размещён: 08.01.2016 02:30

14 плюса

47096 Репутация автора

Этот вопрос связан не с принципом разделения строк , а с тем, как получить n-й элемент .

Все ответы здесь делают какие - то строки расщепления с помощью рекурсии, CTEс, несколько CHARINDEX, REVERSEи PATINDEX, придумав функции, вызов методов CLR, количество таблиц, CROSS APPLYS ... Большинство ответов охватывают множество строк кода.

Но - если вы действительно хотите ничего, кроме подхода к получению n-го элемента, - это можно сделать как реальный однострочный , без UDF, даже не подвыбор ... И в качестве дополнительной выгоды: тип safe

Получите часть 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)')

Конечно, вы можете использовать переменные для разделителя и позиции (использовать sql:columnдля извлечения позиции непосредственно из значения запроса):

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)')

Если ваша строка может содержать запрещенные символы (особенно один из них &><), вы все равно можете сделать это таким образом. Просто используйте FOR XML PATHв своей строке сначала, чтобы заменить все запрещенные символы фиктивной escape-последовательностью неявно.

Это особый случай, если - дополнительно - ваш разделитель - точка с запятой . В этом случае я сначала заменяю разделитель на «# 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)');

ОБНОВЛЕНИЕ для SQL-Server 2016+

К сожалению, разработчики забыли вернуть индекс детали STRING_SPLIT. Но, используя SQL-Server 2016+, есть OPENJSON.

В документации четко указано:

Когда OPENJSON анализирует массив JSON, функция возвращает индексы элементов в тексте JSON в виде ключей.

Строка , как 1,2,3нужно ничего больше , чем скобки: [1,2,3].
Строка слов вроде this is an exampleдолжна быть ["this","is","an"," example"].
Это очень простые операции с строками. Просто попробуйте:

DECLARE @str VARCHAR(100)='Hello John Smith';

SELECT [value]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]')
WHERE [key]=1 --zero-based!
Автор: Shnugo Размещён: 08.07.2016 08:41

1 плюс

1663 Репутация автора

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

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

SELECT value FROM string_split(@string, ',')
Автор: Victor Hugo Terceros Размещён: 04.09.2017 09:52

1 плюс

576 Репутация автора

Для современного подхода, использующего 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-й элемент.

Автор: uzr Размещён: 02.01.2018 03:02

1 плюс

41 Репутация автора

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

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

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
    );
Автор: zipppy Размещён: 22.03.2018 02:38

5 плюса

1655 Репутация автора

Если ваша база данных имеет уровень совместимости 130 или выше, вы можете использовать функцию STRING_SPLIT вместе с предложениями OFFSET FETCH, чтобы получить конкретный элемент по индексу.

Чтобы получить элемент с индексом N (на основе нуля), вы можете использовать следующий код

SELECT value
FROM STRING_SPLIT('Hello John Smith',' ')
ORDER BY (SELECT NULL)
OFFSET N ROWS
FETCH NEXT 1 ROWS ONLY

Чтобы проверить уровень совместимости вашей базы данных , выполните этот код:

SELECT compatibility_level  
FROM sys.databases WHERE name = 'YourDBName';
Автор: Gorgi Rankovski Размещён: 05.04.2018 10:23

1 плюс

48 Репутация автора

Вот функция, которая позволит решить задачу по расщеплению строки и доступу к элементу 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
Автор: VinceL Размещён: 26.04.2018 09:16

1 плюс

126 Репутация автора

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

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
Автор: Sam K Размещён: 20.08.2018 06:59

1 плюс

183 Репутация автора

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

SELECT 
    SUBSTRING(
                SUBSTRING('Hello John Smith' ,0,CHARINDEX(' ','Hello John Smith',CHARINDEX(' ','Hello John Smith')+1)
                        ),CHARINDEX(' ','Hello John Smith'),LEN('Hello John Smith')
            )
Автор: GGadde Размещён: 17.09.2018 09:07
Вопросы из категории :
32x32