Jak podzielić ciąg, aby uzyskać dostęp do elementu x?

493

Używając programu SQL Server, w jaki sposób podzielić ciąg, aby uzyskać dostęp do elementu x?

Weź ciąg „Hello John Smith”. Jak mogę podzielić ciąg według spacji i uzyskać dostęp do elementu o indeksie 1, który powinien zwrócić „John”?

GateKiller
źródło
3
Zobacz stackoverflow.com/questions/314824/... także
Jarrod Dixon
5
wbudowany od serwera SQL 2016 msdn.microsoft.com/en-us/library/mt684588.aspx
Tim Abell
4
Te najwyższe odpowiedzi tutaj są - przynajmniej dla mnie - dość staroświecki i raczej przestarzały. Lokalizacja procedur, pętle, rekurencje, CLR, funkcje, wiele linii kodu ... Interesujące może być przeczytanie „aktywnych” odpowiedzi, aby znaleźć więcej aktualnych metod.
Shnugo,
Dodałem nową odpowiedź z bardziej aktualnym podejściem: stackoverflow.com/a/49669994/632604
Gorgi Rankovski
Spróbuj Pobierz n-ty element listy -> portosql.wordpress.com/2019/05/27/enesimo-elemento-lista
José Diz

Odpowiedzi:

191

Rozwiązanie w funkcji SQL User Defined Function służące do parsowania łańcucha rozdzielanego może być pomocne (z projektu Code ).

Możesz użyć tej prostej logiki:

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
źródło
1
dlaczego SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))nie SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
Beth
12
@GateKiller To rozwiązanie nie obsługuje Unicode i wykorzystuje kodowanie numeryczne na stałe (18,3), co nie czyni z niego realnej funkcji „wielokrotnego użytku”.
Filip De Vos
4
Działa to, ale przydziela dużo pamięci i marnuje procesor.
jjxtra
2
Począwszy od SQL Server 2016, jest teraz wbudowana funkcja STRING_SPLIT, która podzieli ciąg i zwróci wynik tabeli z jedną kolumną, której można użyć w SELECTinstrukcji lub w innym miejscu.
qJake
Szkoda, że ​​faceci, dla których pracuję, nie są w 2016 roku. Będę o tym pamiętać na wypadek, gdyby kiedykolwiek stracili prowadzenie. Świetne rozwiązanie w międzyczasie. Zaimplementowałem to jako funkcję i dodałem ogranicznik jako argument.
Brandon Griffin
355

Nie wierzę, że SQL Server ma wbudowaną funkcję podziału, więc oprócz UDF jedyną znaną odpowiedzią jest przejęcie funkcji PARSENAME:

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

PARSENAME bierze ciąg i dzieli go na znak kropki. Jako drugi argument przyjmuje liczbę, która określa, który segment łańcucha ma zostać zwrócony (działa od tyłu do przodu).

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

Oczywistym problemem jest to, że ciąg zawiera już kropkę. Nadal uważam, że korzystanie z UDF jest najlepszym sposobem ... jakieś inne sugestie?

Nathan Bedford
źródło
102
Dzięki Saul ... Powinienem zaznaczyć, że to rozwiązanie jest naprawdę złym rozwiązaniem dla prawdziwego rozwoju. PARSENAME oczekuje tylko czterech części, więc użycie łańcucha zawierającego więcej niż cztery części powoduje, że zwraca NULL. Rozwiązania UDF są oczywiście lepsze.
Nathan Bedford,
33
To świetny hack, który sprawia, że ​​płaczę, że coś takiego jest niezbędne do czegoś tak cholernie prostego w prawdziwych językach.
Factor Mystic
36
Aby indeksy działały w „prawidłowy” sposób, to znaczy od 1, przejęłem twój porywacz za pomocą REVERSE: REVERSE (PARSENAME (REPLACE (REVERSE ('Hello John Smith'), '', '.')) , 1)) - Returns Hello
NothingsImpossible
3
@FactorMystic Pierwsza normalna forma wymaga, aby nie wstawiać wielu wartości w jednym polu. To dosłownie pierwsza zasada RDBMS. SPLIT()Funkcja nie jest dostarczana, ponieważ zachęca słabe projekt bazy danych, a baza danych nie zostanie zoptymalizowany do korzystania z danych zapisanych w tym formacie. RDBMS nie jest zobowiązany do pomocy programistów robić głupie rzeczy, że zostało zaprojektowane nie do uchwytu. Prawidłowa odpowiedź będzie zawsze brzmiała: „Normalizuj bazę danych, jak mówiliśmy 40 lat temu”. Ani SQL, ani RDBMS nie są winne złego projektu.
Bacon Bits,
8
@BaconBits, chociaż teoretycznie się zgadzam, w praktyce takie narzędzia są przydatne podczas normalizacji złego projektu stworzonego przez kogoś, kto pojawił się przed tobą.
Tim Abell,
110

Najpierw utwórz funkcję (za pomocą CTE wspólne wyrażenie tabelowe eliminuje potrzebę tworzenia tabeli tymczasowej)

 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

Następnie użyj go jako dowolnej tabeli (lub zmodyfikuj w celu dopasowania do istniejącego przechowywanego proc) w ten sposób.

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

Aktualizacja

Poprzednia wersja nie działała dla ciągu wejściowego dłuższego niż 4000 znaków. Ta wersja zajmuje się ograniczeniem:

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

Użycie pozostaje takie samo.

vzczc
źródło
14
Jest elegancki, ale działa tylko na 100 elementów ze względu na limit głębokości rekurencji.
Pking
4
@Pking, nie, domyślnie jest to 100(aby zapobiec nieskończonej pętli). Użyj podpowiedzi MAXRECURSION, aby zdefiniować liczbę poziomów rekurencji ( 0do 32767, 0to „no limit” - może zniszczyć serwer). BTW, znacznie lepsza odpowiedź niż PARSENAME, ponieważ jest uniwersalna :-). +1
Michał Powaga
Dodając maxrecursiondo tego rozwiązania, pamiętaj o tym pytaniu i jego odpowiedziach. Jak ustawić maxrecursionopcję CTE w funkcji wycenianej w tabeli .
Michał Powaga,
W szczególności odwołaj się do odpowiedzi Crisfole'a - jego metoda nieco ją spowalnia, ale jest prostsza niż większość innych opcji.
AHiggins
drobny punkt, ale użycie nie pozostaje takie samo, ponieważ zmieniłeś nazwę kolumny, więc snie jest już zdefiniowany
Tim Abell
62

Większość rozwiązań tutaj wykorzystuje pętle while lub rekurencyjne CTE. Podejście oparte na zestawie będzie lepsze, obiecuję, jeśli możesz użyć separatora innego niż spacja:

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

Przykładowe użycie:

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

Wyniki:

----
blat

Możesz także dodać idxżądaną funkcję jako argument do funkcji, ale zostawię to jako ćwiczenie czytelnikowi.

Nie można tego zrobić tylko z funkcją natywnąSTRING_SPLIT dodaną w SQL Server 2016, ponieważ nie ma gwarancji, że dane wyjściowe będą renderowane w kolejności oryginalnej listy. Innymi słowy, jeśli zdasz, 3,6,1wynik będzie prawdopodobnie w tej kolejności, ale może być 1,3,6. Poprosiłem o pomoc społeczności w ulepszeniu wbudowanej funkcji tutaj:

Przy wystarczającej jakościowej informacji zwrotnej mogą rozważyć wprowadzenie niektórych z tych ulepszeń:

Więcej informacji na temat funkcji podziału, dlaczego (i udowodnij to), podczas gdy pętle i rekurencyjne CTE nie skalują się, i lepsze alternatywy, jeśli dzielenie ciągów pochodzących z warstwy aplikacji:

Jednak w SQL Server 2016 lub nowszym powinieneś przyjrzeć się STRING_SPLIT()i STRING_AGG():

Aaron Bertrand
źródło
1
Najlepsza odpowiedź, IMHO. W niektórych innych odpowiedziach pojawia się problem limitu rekurencji SQL wynoszącego 100, ale nie w tym przypadku. Bardzo szybka i bardzo prosta implementacja. Gdzie jest przycisk +2?
T-moty
5
Wypróbowałem tę funkcję dosłownie przy użyciu: select * from DBO.SplitString('Hello John smith', ' ');i uzyskano wynik: Wartość Hello ello llo lo o John ohn hn n smith mith ith th
wwmbes
2
@AaronBertrand Oryginalny problem opublikowany przez GateKiller dotyczy separatora spacji.
wwmbes
1
@ user1255933 Adresowany.
Aaron Bertrand,
1
@Michael Tak, to prawda. Nie miałbyś też tabeli do wyboru, gdybyś nie miał uprawnienia ALTER SCHEMA, i nie byłby w stanie wybrać z niej, jeśli nie masz uprawnienia SELECT. Zawsze możesz poprosić kogoś o utworzenie dla ciebie funkcji . Lub stwórz go gdzieś, gdzie możesz go stworzyć (nawet tymczasowo, powiedzmy w tempdb). A w 2016+ powinieneś używać STRING_SPLIT (), a nie funkcji, którą i tak musisz sam stworzyć.
Aaron Bertrand
38

Możesz wykorzystać tabelę liczb do parsowania łańcucha.

Utwórz tabelę liczb fizycznych:

    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

Utwórz tabelę testową z 1000000 wierszami

    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

Utwórz funkcję

    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

Użycie (wyprowadza 3mil wierszy w 40s na moim laptopie)

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

sprzątać

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

Wydajność tutaj nie jest niesamowita, ale wywołanie funkcji w tabeli zawierającej milion wierszy nie jest najlepszym pomysłem. Wykonując ciąg podzielony na wiele wierszy, unikałbym tej funkcji.

Nathan Skerl
źródło
2
Najlepsze rozwiązanie IMO, pozostałe mają pewne ograniczenia. Jest to szybkie i może analizować długie łańcuchy z wieloma elementami.
Pking
Dlaczego zamawiasz n malejąco? Jeśli są tam trzy elementy, a my zaczynamy numerację od 1, to pierwszy element będzie numerem 3, a ostatni numerem 1. Czy nie dałoby to bardziej intuicyjnych wyników, gdyby desczostały usunięte?
topór - wykonane SOverflow
1
Uzgodnione, byłoby bardziej intuicyjne w kierunku asc. Postępowałem zgodnie z konwencją parsename (), która używa desc
Nathan Skerl
3
wyjaśnienie, jak to działa, byłoby świetne
Tim Abell,
W teście na 100 milionach wierszy do 3 pól do parsowania ufn_ParseArray nie zakończył się po 25 minutach, a REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) od @NothingsImpossible zakończono w 1,5 minuty. @hello_earth Jak Twoje rozwiązanie porównałoby się z dłuższymi ciągami z więcej niż 4 polami?
wwmbes
31

To pytanie nie dotyczy podejścia opartego na dzieleniu łańcucha , ale sposobu uzyskania n-tego elementu .

Wszystkie odpowiedzi są tu robi jakieś rozszczepienie łańcucha za pomocą rekurencji, CTEs, stwardnienie CHARINDEX, REVERSEa PATINDEXfunkcje wymyślanie, wezwanie do metod numerycznych CLR, stoły, CROSS APPLYs ... Większość odpowiedzi obejmują wiele linii kodu.

Ale - jeśli naprawdę nie chcesz niczego więcej niż podejście do uzyskania n-tego elementu - można to zrobić jako prawdziwy jeden liniowiec , bez UDF, nawet bez wyboru podrzędnego ... I jako dodatkowa korzyść: wpisz bezpieczny

Uzyskaj część 2 oddzieloną spacją:

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

Oczywiście możesz użyć zmiennych dla separatora i pozycji (użyj, sql:columnaby pobrać pozycję bezpośrednio z wartości zapytania):

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

Jeśli Twój ciąg może zawierać niedozwolone znaki (szczególnie jeden spośród &><), nadal możesz to zrobić w ten sposób. Po prostu użyj FOR XML PATHnajpierw swojego ciągu, aby domyślnie zastąpić wszystkie zabronione znaki pasującą sekwencją ucieczki.

Jest to bardzo szczególny przypadek, jeśli - dodatkowo - separatorem jest średnik . W takim przypadku najpierw zastępuję separator na „# DLMT #”, a na końcu zastępuję go tagami 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)');

AKTUALIZACJA dla SQL-Server 2016+

Niestety programiści zapomnieli zwrócić indeks części STRING_SPLIT. Ale przy użyciu SQL Server 2016+ istnieje JSON_VALUEi OPENJSON.

Za pomocą JSON_VALUEmożemy podać pozycję jako tablicę indeksu.

Dla dokumentacja stwierdza wyraźnie:OPENJSON

Kiedy OPENJSON analizuje tablicę JSON, funkcja zwraca indeksy elementów w tekście JSON jako klucze.

Ciąg jak 1,2,3potrzebuje niczego więcej niż nawiasach: [1,2,3].
Ciąg takich słów this is an examplemusi być ["this","is","an","example"].
Są to bardzo łatwe operacje na łańcuchach. Po prostu wypróbuj:

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

--We can build the json-path '$[1]' using CONCAT
SELECT JSON_VALUE('["' + REPLACE(@str,' ','","') + '"]',CONCAT('$[',@position-1,']'));

- Zobacz to dla dzielnika łańcuchów bezpiecznego pozycjonowania (od zera ):

SELECT  JsonArray.[key] AS [Position]
       ,JsonArray.[value] AS [Part]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]') JsonArray

W tym poście przetestowałem różne podejścia i stwierdziłem, że OPENJSONjest to naprawdę szybkie. Nawet znacznie szybszy niż słynna metoda „delimitedSplit8k ()” ...

AKTUALIZACJA 2 - Uzyskaj wartości bezpieczne dla typu

Możemy użyć tablicy w tablicy po prostu używając podwójnej [[]]. Pozwala to na WITHwpisanie klauzuli:

DECLARE  @SomeDelimitedString VARCHAR(100)='part1|1|20190920';

DECLARE @JsonArray NVARCHAR(MAX)=CONCAT('[["',REPLACE(@SomeDelimitedString,'|','","'),'"]]');

SELECT @SomeDelimitedString          AS TheOriginal
      ,@JsonArray                    AS TransformedToJSON
      ,ValuesFromTheArray.*
FROM OPENJSON(@JsonArray)
WITH(TheFirstFragment  VARCHAR(100) '$[0]'
    ,TheSecondFragment INT          '$[1]'
    ,TheThirdFragment  DATE         '$[2]') ValuesFromTheArray
Shnugo
źródło
Re: jeśli Twój ciąg może zawierać niedozwolone znaki ... możesz po prostu owinąć podłańcuchy w ten sposób <x><![CDATA[x<&>x]]></x>.
Salman A
@SalmanA, tak, -sekcje CDATAmogą sobie z tym poradzić ... Ale po obsadzie zniknęły (zmieniono na ucieczkę text()niejawnie). Nie podoba mi się magia pod maską , więc wolę (SELECT 'Text with <&>' AS [*] FOR XML PATH(''))podejście -. Wydaje mi się to czystsze i tak się dzieje ... (Więcej o CDATA i XML ).
Shnugo
22

Oto UDF, który to zrobi. Zwróci tabelę wartości rozdzielanych, nie wypróbowałem na niej wszystkich scenariuszy, ale twój przykład działa dobrze.


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

Nazwałbyś to tak:


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

Edycja: Zaktualizowano rozwiązanie do obsługi ograniczników z długością> 1 jak w:


select * From SplitString('Hello**John**Smith','**')
brendan
źródło
Nie działał dla select * z dbo.ethos_SplitString_fn („facet, knoty, był tutaj”, „,”) część id ----------- ------------ -------------------------------------- 1 facet 2 knot
facet
2
uważaj za pomocą len (), ponieważ nie zwróci poprawnej liczby, jeśli jej argument ma końcowe spacje., np. len ('-') = 2.
Rory
Nie działa na: wybierz * z dbo.SplitString ('foo, foo test ,,,, foo', ',')
cbp
1
Poprawka dla cbp .. Wybierz @myString = substring (@ mystring, @ iSpaces + len (@dinstallator), len (@myString) - charindex (@ deliminator, @ myString, 0))
Alxwest
16

Tutaj zamieszczam prosty sposób rozwiązania

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


Wykonaj taką funkcję

  select * from dbo.split('Hello John Smith',' ')
Sivaganesh Tamilvendhan
źródło
Podobało mi się to rozwiązanie. Rozwinął go, aby zwracał wartość skalarną na podstawie określonej kolumny w wynikach.
Alan
Zostałem spalony z „&” w ciągu, który ma zostać podzielony za pomocą tego
KeithL
10

Moim zdaniem, robicie to zbyt skomplikowane. Wystarczy utworzyć CLR UDF i gotowe.

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
źródło
20
Wydaje mi się, że jest to zbyt skomplikowane, ponieważ muszę mieć Visual Studio, następnie włączyć CLR na serwerze, a następnie utworzyć i skompilować projekt, a na koniec dodać zestawy do bazy danych, aby z niej skorzystać. Ale nadal jest interesującą odpowiedzią.
Guillermo Gutiérrez
3
@ guillegr123, nie musi być skomplikowane. Możesz po prostu pobrać i zainstalować (za darmo!) SQL #, który jest biblioteką funkcji i procedur SQLCLR. Możesz go pobrać z SQLsharp.com . Tak, jestem autorem, ale String_Split jest zawarty w wersji darmowej.
Solomon Rutzky
10

Co z używaniem stringivalues() oświadczeniem?

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

Zestaw wyników osiągnięty.

id  item
1   Hello
2   John
3   Smith
Frederic
źródło
1
użyłem twojej odpowiedzi, ale nie zadziałało, ale zmodyfikowałem i to działało ze związkiem wszystkie, używam sql 2005
Angel
9

Korzystam z odpowiedzi frederic, ale to nie działało w SQL Server 2005

I zmodyfikowano go i używam selectz union alli działa

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

A zestaw wyników to:

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you
anioł
źródło
To jest naprawdę świetne, jakie kiedykolwiek widziałem w sql rzeczy, zadziałało dla mojej pracy i doceniam to, dzięki!
Abdurrahman I.
Byłem naprawdę podekscytowany, kiedy to zobaczyłem, ponieważ wyglądało to tak czyste i łatwe do zrozumienia, ale niestety nie możesz umieścić tego w UDF z powodu EXEC. EXECniejawnie wywołuje procedurę przechowywaną i nie można używać procedur przechowywanych w UDF.
Kristen Hammack,
To działa idealnie !! Chciałem użyć funkcji (SplitStrings_Moden) stąd: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments , która to robi i zajęło półtorej minuty na podzielenie danych i powrót wiersze przy użyciu tylko 4 numerów kont. Przetestowałem twoją wersję z lewym złączeniem na stole z danymi o numerach kont i zajęło to 2 lub 3 sekundy! Ogromna różnica i działa bezbłędnie! Dałbym to 20 głosów, jeśli to możliwe!
MattE
8

Ten wzór działa dobrze i możesz uogólniać

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

uwaga POLA , INDEKS i TYP .

Niech trochę tabela z identyfikatorami, takimi jak

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

Następnie możesz pisać

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

dzielenie i odlewanie wszystkich części.

josejuan
źródło
Jest to jedyne rozwiązanie, które pozwala rzutować na określone typy i jest umiarkowanie wydajne (CLR nadal jest najbardziej wydajny, ale to podejście obsługuje tabelę wierszy 8 GB, 10 tokenów, 10 M w około 9 minut (serwer aws m3, 4k Iops) Provided Drive)
Andrew Hill,
7

Jeśli Twoja baza danych ma poziom zgodności 130 lub wyższy, możesz użyć funkcji STRING_SPLIT wraz z klauzulami OFFSET FETCH , aby uzyskać określony element według indeksu.

Aby uzyskać pozycję o indeksie N (zero), możesz użyć następującego kodu

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

Aby sprawdzić poziom zgodności bazy danych , wykonaj ten kod:

SELECT compatibility_level  
FROM sys.databases WHERE name = 'YourDBName';
Gorgi Rankovski
źródło
Sztuką jest OFFSET 1 ROWS, która pominie pierwszy przedmiot i zwróci drugi. Jeśli twoje indeksy są oparte na 0, a @X jest zmienną zawierającą indeks pozycji, który chcesz pobrać, możesz na pewno wykonać OFFSET @X ROWS
Gorgi Rankovski
Okej, nie xmlużywałam tego wcześniej ... Dobrze wiedzieć ... Nadal wolałbym podejście oparte na -split, ponieważ pozwala ono pobrać wartość bezpieczną dla typu i nie wymaga sub-zapytania, ale jest to dobry. +1 z mojej strony
Shnugo,
3
problem polega na tym, że STRING_SPLIT nie gwarantuje kolejności zwracanych wyników. Więc twoja pozycja 1 może, ale nie musi, być moją pozycją 1.
użytkownik1443098
@GorgiRankovski, Korzystanie z STRING_SPLITżądań dla v2016 +. W takim przypadku znacznie lepiej jest użyć OPENJSONlub JSON_VALUE. Może chcesz sprawdzić moją odpowiedź
Shnugo,
6

Szukałem rozwiązania w sieci, a poniższe działa dla mnie. Nr ref .

I wywołujesz funkcję w ten sposób:

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
źródło
Za pomocą tej funkcji nie można łatwo uzyskać dostępu do n-tego elementu.
Björn Lindqvist
6

Jeszcze inna zdobądź n-tą część ciągu przez funkcję delimera:

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

i wykorzystanie:

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

który zwraca:

c
Ramazan Binarbasi
źródło
Podoba mi się to rozwiązanie jako opcja zwracania pojedynczego podciągu, a nie otrzymywania przeanalizowanej tabeli, z której następnie musisz wybrać. Zastosowanie tabeli ma swoje zastosowanie, ale do tego, czego potrzebowałem, zadziałało idealnie.
James H
5

Spróbuj tego:

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

Przetestuj tak:

select * from SplitWordList('Hello John Smith')
Seibar
źródło
Przeszedłem przez to i to jest dokładnie tak, jak chcę! nawet ja mogę go dostosować do ignorowania wybranych przeze mnie znaków specjalnych!
Vikas,
5

W poniższym przykładzie użyto rekurencyjnego CTE

Aktualizacja 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

Demo na SQLFiddle

Aleksandr Fedorenko
źródło
2


    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
źródło
2

Możesz podzielić ciąg znaków w SQL bez potrzeby używania funkcji:

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

Jeśli potrzebujesz obsługi dowolnych ciągów znaków (ze znakami specjalnymi 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
źródło
1

Wiem, że to stare pytanie, ale myślę, że ktoś może skorzystać z mojego rozwiązania.

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

FIDDLE SQL

Zalety:

  • Oddziela wszystkie 3 ograniczniki podciągu przez „”.
  • Nie wolno używać pętli while, ponieważ zmniejsza to wydajność.
  • Nie ma potrzeby przestawiania, ponieważ cały wynikowy łańcuch podrzędny zostanie wyświetlony w jednym wierszu

Ograniczenia:

  • Trzeba znać całkowitą liczbę nie. spacji (podciąg).

Uwaga : rozwiązanie może dać ciąg podrzędny do N.

Aby pokonać ograniczenie, możemy skorzystać z następującego ref .

Ale znowu powyższego rozwiązania nie można użyć w tabeli (Actaully nie byłem w stanie go użyć).

Ponownie mam nadzieję, że to rozwiązanie może komuś pomóc.

Aktualizacja: W przypadku rekordów> 50000 nie zaleca się używania, LOOPSponieważ spowoduje to obniżenie wydajności

Luv
źródło
1

Rozwiązanie oparte na czystym zestawie TVFz rekurencyjnym CTE. Możesz JOINi APPLYta funkcja do dowolnego zestawu danych.

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

Stosowanie:

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

Wynik:

value   index
-------------
John    1
Andrey Morozov
źródło
1

Prawie wszystkie inne odpowiedzi zastępują dzielony ciąg, który marnuje cykle procesora i wykonuje niepotrzebne przydziały pamięci.

Omawiam tutaj znacznie lepszy sposób wykonania podziału strun: http://www.digitalruby.com/split-string-sql-server/

Oto kod:

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
źródło
0

Rekurencyjne rozwiązanie CTE z bólem serwera, przetestuj je

Konfiguracja schematu MS SQL Server 2008 :

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

Zapytanie 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

Wyniki :

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |
Dani Herrera
źródło
0

chociaż podobny do odpowiedzi opartej na xml autorstwa josejuan, stwierdziłem, że przetwarzanie ścieżki xml tylko raz, następnie przestawianie było umiarkowanie bardziej wydajne:

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

pobiegł w 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

pobiegł w 9:20

Andrew Hill
źródło
0
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

I UŻYWAJ GO

select *from dbo.fnSplitString('Querying SQL Server','')
Savas Adar
źródło
0

jeśli ktoś chce uzyskać tylko jedną część oddzielnego tekstu, może to wykorzystać

wybierz * z 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
  )
nazim hatipoglu
źródło
0

Zdewoluowałem to,

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

jedyną uwagę, na którą powinieneś zwrócić uwagę, jest kropka ”. koniec @x zawsze powinien być na miejscu.

Ali CAKIL
źródło
0

Opierając się na @NothingsImpossible rozwiązanie, a raczej komentować najczęściej głosowaną odpowiedź (tuż poniżej zaakceptowanej), znalazłem następujący szybki i brudny rozwiązanie, które spełnia moje własne potrzeby - ma tę zaletę, że znajduje się wyłącznie w domenie SQL.

biorąc ciąg „pierwszy; drugi; trzeci; czwarty; piąty”, powiedzmy, chcę uzyskać trzeci token. działa to tylko wtedy, gdy wiemy, ile tokenów będzie miał łańcuch - w tym przypadku jest to 5. więc moim sposobem działania jest odrąbanie dwóch ostatnich tokenów (zapytanie wewnętrzne), a następnie oderwanie dwóch pierwszych tokenów ( zapytanie zewnętrzne)

wiem, że jest to brzydkie i obejmuje określone warunki, w których byłem, ale publikuję to na wypadek, gdyby ktoś uznał to za przydatne. Twoje zdrowie

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
hello_earth
źródło
działa to tylko wtedy, gdy wiemy, ile tokenów będzie miał łańcuch - przełamujące ograniczenie ...
Shnugo,
0
declare @strng varchar(max)='hello john smith'
select (
    substring(
        @strng,
        charindex(' ', @strng) + 1,
        (
          (charindex(' ', @strng, charindex(' ', @strng) + 1))
          - charindex(' ',@strng)
        )
    ))
Smart003
źródło
0

Począwszy od SQL Server 2016 my string_split

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

SELECT value FROM string_split(@string, ',')
Victor Hugo Terceros
źródło
To dobrze i dobrze, ale nie rozwiązuje problemu uzyskania n-tego wyniku.
Johnie Karr
STRING_SPLITnie gwarantuje zwrotu tego samego zamówienia. Ale OPENJSONtak (patrz moja odpowiedź (sekcja aktualizacji) )
Shnugo,