Wie teile ich eine Zeichenfolge, damit ich auf Element x zugreifen kann?


493

Wie teile ich unter Verwendung von SQL Server eine Zeichenfolge, damit ich auf Element x zugreifen kann?

Nehmen Sie eine Zeichenfolge "Hallo John Smith". Wie kann ich die Zeichenfolge nach Leerzeichen aufteilen und auf das Element in Index 1 zugreifen, das "John" zurückgeben soll?




4
Die höchsten Antworten hier sind - zumindest für mich - ziemlich altmodisch und ziemlich veraltet. Prozedurale Lokalisierung, Schleifen, Rekursionen, CLR, Funktionen, viele Codezeilen ... Es könnte interessant sein, die "aktiven" Antworten zu lesen, um aktuellere Ansätze zu finden.
Shnugo

Ich habe eine neue Antwort mit einem aktuelleren Ansatz hinzugefügt
Gorgi Rankovski

Versuchen Sie, das n-te Element einer Liste abzurufen
José Diz

Antworten:


191

Möglicherweise finden Sie die Lösung in SQL User Defined Function zum Parsen einer durch Trennzeichen getrennten Zeichenfolge hilfreich (aus The Code Project ).

Sie können diese einfache Logik verwenden:

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

1
warum SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))nicht SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
Beth

12
@GateKiller Diese Lösung unterstützt Unicode nicht und verwendet fest codierte Zahlen (18,3), was sie nicht zu einer brauchbaren "wiederverwendbaren" Funktion macht.
Filip De Vos

4
Dies funktioniert, weist jedoch viel Speicher zu und verschwendet CPU.
jjxtra

2
Ab SQL Server 2016 gibt es jetzt eine integrierte Funktion STRING_SPLIT, die eine Zeichenfolge aufteilt und ein einspaltiges Tabellenergebnis zurückgibt, das Sie in einer SELECTAnweisung oder an anderer Stelle verwenden können.
QJake

Schade, dass die Jungs, für die ich arbeite, nicht im Jahr 2016 sind. Aber ich werde es mir merken, falls sie jemals die Führung aus ihren Schuhen bekommen sollten. Tolle Lösung in der Zwischenzeit. Ich habe es als Funktion implementiert und Trennzeichen als Argument hinzugefügt.
Brandon Griffin

355

Ich glaube nicht, dass SQL Server über eine integrierte Split-Funktion verfügt. Abgesehen von einer UDF ist die einzige andere Antwort, die ich kenne, die Entführung der PARSENAME-Funktion:

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

PARSENAME nimmt eine Zeichenfolge und teilt sie in das Punktzeichen auf. Als zweites Argument wird eine Zahl verwendet, und diese Zahl gibt an, welches Segment der Zeichenfolge zurückgegeben werden soll (von hinten nach vorne).

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

Offensichtliches Problem ist, wenn die Zeichenfolge bereits einen Punkt enthält. Ich denke immer noch, dass die Verwendung eines UDF der beste Weg ist ... irgendwelche anderen Vorschläge?


102
Danke Saul ... Ich sollte darauf hinweisen, dass diese Lösung wirklich eine schlechte Lösung für echte Entwicklung ist. PARSENAME erwartet nur vier Teile. Wenn Sie also einen String mit mehr als vier Teilen verwenden, wird NULL zurückgegeben. Die UDF-Lösungen sind offensichtlich besser.
Nathan Bedford

33
Dies ist ein großartiger Hack und bringt mich auch zum Weinen, dass so etwas für etwas so verdammt Einfaches in echten Sprachen notwendig ist.
Factor Mystic

36
Damit die Indizes "richtig" funktionieren, dh ab 1, habe ich Ihren Hijack mit REVERSE entführt: REVERSE (PARSENAME (REPLACE (REVERSE ('Hallo John Smith'), '', '.') , 1)) - Gibt Hallo zurück
NothingsImpossible

3
@FactorMystic First Normal Form erfordert, dass Sie nicht mehrere Werte in ein einzelnes Feld einfügen . Es ist buchstäblich die erste Regel eines RDBMS. Eine SPLIT()Funktion wird nicht bereitgestellt, da sie ein schlechtes Datenbankdesign fördert und die Datenbank niemals für die Verwendung von Daten optimiert wird, die in diesem Format gespeichert sind. Das RDBMS ist nicht verpflichtet, Entwicklern dabei zu helfen, dumme Dinge zu tun, für die es entwickelt wurde, um es nicht zu handhaben. Die richtige Antwort lautet immer "Normalisieren Sie Ihre Datenbank so, wie wir es Ihnen vor 40 Jahren gesagt haben." Weder SQL noch das RDBMS sind für schlechtes Design verantwortlich.
Bacon Bits

8
@BaconBits Während ich theoretisch zustimme, sind solche Tools in der Praxis nützlich, wenn Sie ein schlechtes Design normalisieren, das von jemandem erstellt wurde, der vor Ihnen kam.
Tim Abell

110

Erstellen Sie zunächst eine Funktion (mit CTE macht der allgemeine Tabellenausdruck die Notwendigkeit einer temporären Tabelle überflüssig).

 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

Verwenden Sie es dann wie eine beliebige Tabelle (oder ändern Sie es so, dass es in Ihren vorhandenen gespeicherten Prozess passt).

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

Aktualisieren

Die vorherige Version schlug für Eingabezeichenfolgen mit mehr als 4000 Zeichen fehl. Diese Version kümmert sich um die Einschränkung:

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

Die Nutzung bleibt gleich.


14
Es ist elegant, funktioniert aber aufgrund der begrenzten Rekursionstiefe nur für 100 Elemente.
Pking

4
@Pking, nein, die Standardeinstellung ist 100(um eine Endlosschleife zu verhindern). Verwenden Sie den MAXRECURSION-Hinweis , um die Anzahl der Rekursionsstufen zu definieren ( 0to 32767, 0ist "no limit" - kann den Server beschädigen ). Übrigens, viel bessere Antwort als PARSENAME, weil es universell ist :-). +1
Michał Powaga

Beachten Sie maxrecursionbeim Hinzufügen zu dieser Lösung diese Frage und ihre Antworten. So richten Sie die maxrecursionOption für einen CTE in einer Tabellenwertfunktion ein .
Michał Powaga

Verweisen Sie speziell auf die Antwort von Crisfole - seine Methode verlangsamt sie etwas, ist aber einfacher als die meisten anderen Optionen.
AHiggins

kleiner Punkt, aber die Verwendung bleibt nicht gleich, da Sie den Spaltennamen geändert haben und daher snicht mehr definiert sind
Tim Abell

62

Die meisten der hier verwendeten Lösungen verwenden while-Schleifen oder rekursive CTEs. Ich verspreche, dass ein satzbasierter Ansatz überlegen ist, wenn Sie ein anderes Trennzeichen als ein Leerzeichen verwenden können:

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

Beispielnutzung:

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

Ergebnisse:

----
blat

Sie können das idxgewünschte Element auch als Argument zur Funktion hinzufügen , aber das überlasse ich dem Leser als Übung.

Sie können dies nicht tun , mit nur der nativen STRING_SPLITFunktion hinzugefügt in SQL Server 2016, weil es keine Garantie dafür gibt , dass die Ausgabe in der Reihenfolge der Originalliste wiedergegeben wird. Mit anderen Worten, wenn Sie 3,6,1das Ergebnis übergeben, wird es wahrscheinlich in dieser Reihenfolge sein, aber es könnte sein 1,3,6. Ich habe hier um die Hilfe der Community bei der Verbesserung der integrierten Funktion gebeten:

Bei ausreichendem qualitativen Feedback können sie tatsächlich einige dieser Verbesserungen in Betracht ziehen:

Weitere Informationen zu Teilungsfunktionen, warum (und beweisen Sie dies), während Schleifen und rekursive CTEs nicht skaliert werden, und bessere Alternativen, wenn Zeichenfolgen aus der Anwendungsschicht aufgeteilt werden:

Auf SQL Server 2016 oder höher, obwohl sollte man sich STRING_SPLIT()und STRING_AGG():


1
Beste Antwort, IMHO. In einigen anderen Antworten gibt es das Problem der SQL-Rekursionsgrenze von 100, in diesem Fall jedoch nicht. Sehr schnelle und sehr einfache Implementierung. Wo ist die +2 Taste?
T-Moty

5
Ich habe diese Funktion wörtlich mit der Verwendung ausprobiert: select * from DBO.SplitString('Hello John smith', ' ');und die erzeugte Ausgabe war: Wert Hallo ello llo lo o John ohn hn n
smith

2
@AaronBertrand Das ursprüngliche Problem von GateKiller betrifft ein Leerzeichen.
wwmbes

1
@ user1255933 Adressiert.
Aaron Bertrand

1
@ Michael Ja, das stimmt. Sie hätten auch keine Tabelle zur Auswahl, wenn Sie nicht über die Berechtigung ALTER SCHEMA verfügen, und könnten nicht aus dieser Tabelle auswählen, wenn Sie nicht über die Berechtigung SELECT verfügen. Sie könnten jederzeit jemanden bitten , die Funktion für Sie zu erstellen . Oder erstellen Sie es an einem Ort, an dem Sie es erstellen können (auch vorübergehend, z. B. in Tempdb). Und ab 2016 sollten Sie STRING_SPLIT () verwenden und keine Funktion, die Sie sowieso selbst erstellen müssen.
Aaron Bertrand

38

Sie können eine Zahlentabelle nutzen, um die Zeichenfolge zu analysieren.

Erstellen Sie eine Tabelle mit physischen Zahlen:

    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

Erstellen Sie eine Testtabelle mit 1000000 Zeilen

    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

Erstellen Sie die Funktion

    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

Verwendung (gibt 3mil Zeilen in 40s auf meinem Laptop aus)

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

Aufräumen

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

Die Leistung hier ist nicht erstaunlich, aber das Aufrufen einer Funktion über eine Million Zeilentabellen ist nicht die beste Idee. Wenn ich eine Zeichenfolge auf mehrere Zeilen verteilen würde, würde ich die Funktion vermeiden.


2
Die beste Lösung IMO, die anderen haben eine Art Einschränkung. Dies ist schnell und kann lange Zeichenfolgen mit vielen Elementen analysieren.
Pking

Warum bestellen Sie n absteigend? Wenn dort drei Elemente vorhanden sind und wir mit der Nummerierung bei 1 begonnen haben, ist das erste Element die Nummer 3 und das letzte die Nummer 1. Wäre es nicht intuitiver, wenn die Elemente descentfernt würden?
Beil - fertig mit SOverflow

1
Einverstanden, wäre in aufsteigender Richtung intuitiver. Ich folgte parsename () Konvention, die desc
Nathan Skerl

3
Eine Erklärung, wie das funktioniert, wäre großartig
Tim Abell,

In einem Test mit 100 Millionen Zeilen mit bis zu 3 zu analysierenden Feldern wurde ufn_ParseArray nach 25 Minuten nicht beendet, während REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) @NothingsImpossible in 1,5 Minuten abgeschlossen war. @hello_earth Wie würde sich Ihre Lösung bei längeren Zeichenfolgen mit mehr als 4 Feldern vergleichen lassen?
wwmbes

31

Bei dieser Frage geht es nicht um einen String-Split-Ansatz , sondern darum, wie das n-te Element erhalten wird .

Alle Antworten hier sind eine Art von String - Splitting zu tun Rekursion, CTEs, mehrere CHARINDEX, REVERSEund PATINDEX, erfinden Funktionen, Aufruf für CLR Methoden, Zahlentabellen, CROSS APPLYs ... Die meisten Antworten viele Zeilen Code.

Aber - wenn Sie wirklich nur einen Ansatz wollen, um das n-te Element zu erhalten - kann dies als echter Einzeiler , ohne UDF, nicht einmal als Unterauswahl erfolgen ... und als zusätzlichen Vorteil: Typensicher

Holen Sie sich Teil 2 durch ein Leerzeichen begrenzt:

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

Natürlich können Sie Variablen für Trennzeichen und Position verwenden (verwenden Sie sql:columndiese Option, um die Position direkt aus dem Wert einer Abfrage abzurufen):

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

Wenn Ihre Zeichenfolge möglicherweise verbotene Zeichen enthält (insbesondere eines davon &><), können Sie dies dennoch auf diese Weise tun. Verwenden FOR XML PATHSie einfach zuerst Ihre Zeichenfolge, um alle verbotenen Zeichen implizit durch die passende Escape-Sequenz zu ersetzen.

Es ist ein ganz besonderer Fall, wenn - zusätzlich - Ihr Trennzeichen das Semikolon ist . In diesem Fall ersetze ich das Trennzeichen zuerst durch '# DLMT #' und ersetze es schließlich durch die XML-Tags:

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

UPDATE für SQL-Server 2016+

Leider haben die Entwickler vergessen, den Index des Teils mit zurückzugeben STRING_SPLIT. Bei Verwendung von SQL-Server 2016+ gibt es jedoch JSON_VALUEund OPENJSON.

Mit können JSON_VALUEwir die Position als Index-Array übergeben.

In OPENJSONder Dokumentation heißt es deutlich:

Wenn OPENJSON ein JSON-Array analysiert, gibt die Funktion die Indizes der Elemente im JSON-Text als Schlüssel zurück.

Ein String wie 1,2,3braucht nichts weiter als Klammern : [1,2,3].
Eine Reihe von Wörtern wie this is an examplemuss sein ["this","is","an","example"].
Dies sind sehr einfache Zeichenfolgenoperationen. Probieren Sie es einfach aus:

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,']'));

--See dies für eine sichere Position strangTeiler ( Null-Basis ):

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

In diesem Beitrag habe ich verschiedene Ansätze getestet und festgestellt, dass OPENJSONdas sehr schnell geht. Noch viel schneller als die berühmte Methode "delimitedSplit8k ()" ...

UPDATE 2 - Holen Sie sich die Werte typsicher

Wir können ein Array innerhalb eines Arrays verwenden , indem wir einfach double verwenden [[]]. Dies ermöglicht eine typisierte WITHKlausel:

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

Betreff: Wenn Ihre Zeichenfolge möglicherweise verbotene Zeichen enthält, können Sie die Teilzeichenfolgen einfach so umbrechen <x><![CDATA[x<&>x]]></x>.
Salman A

@SalmanA, ja, CDATAAbschnitte können sich auch damit befassen ... Aber nach der Besetzung sind sie weg (geändert, um text()implizit entkommen zu sein ). Ich mag keine Magie unter der Haube , deshalb würde ich den (SELECT 'Text with <&>' AS [*] FOR XML PATH(''))Ansatz vorziehen . Das sieht für mich sauberer aus und passiert trotzdem ... (Noch etwas zu CDATA und XML ).
Shnugo

22

Hier ist eine UDF, die das macht. Es wird eine Tabelle mit den begrenzten Werten zurückgegeben. Es wurden nicht alle Szenarien ausprobiert, aber Ihr Beispiel funktioniert einwandfrei.


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

Sie würden es so nennen:


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

Bearbeiten: Aktualisierte Lösung zur Behandlung von Trennzeichen mit einer Länge> 1 wie in:


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

Funktionierte nicht für select * aus dbo.ethos_SplitString_fn ('Kerl, Dochte, war hier', ',') ID-Teil ----------- ------------ -------------------------------------- 1 Kerl 2 Docht
Kerl

2
Achten Sie auf len (), da es keine korrekte Zahl zurückgibt, wenn das Argument nachgestellte Leerzeichen enthält, z. B. len ('-') = 2.
Rory

Funktioniert nicht bei: Wählen Sie * aus dbo.SplitString ('foo, foo test ,,,, foo', ',')
cbp

1
Fix für cbp .. Wählen Sie @myString = Teilzeichenfolge (@ mystring, @ iSpaces + len (@deliminator), len (@myString) - charindex (@ deliminator, @ myString, 0))
Alxwest

16

Hier poste ich einen einfachen Lösungsweg

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


Führen Sie die Funktion folgendermaßen aus

  select * from dbo.split('Hello John Smith',' ')

Diese Lösung hat mir gefallen. Es wurde erweitert, um einen Skalarwert basierend auf der angegebenen Spalte in den Ergebnissen zurückzugeben.
Alan

Ich wurde mit einem '&' in der Zeichenfolge verbrannt, um damit geteilt zu werden
KeithL

10

Meiner Meinung nach macht ihr es viel zu kompliziert. Erstellen Sie einfach eine CLR-UDF und fertig.

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

20
Ich denke, das ist zu kompliziert, weil ich Visual Studio haben muss, dann CLR auf dem Server aktivieren, dann das Projekt erstellen und kompilieren und schließlich die Assemblys zur Datenbank hinzufügen muss, um es verwenden zu können. Aber immer noch ist eine interessante Antwort.
Guillermo Gutiérrez

3
@ guillegr123, es muss nicht kompliziert sein. Sie können einfach SQL # herunterladen und installieren (kostenlos!), Eine Bibliothek mit SQLCLR-Funktionen und -Prozessen. Sie können es von SQLsharp.com erhalten . Ja, ich bin der Autor, aber String_Split ist in der kostenlosen Version enthalten.
Solomon Rutzky

10

Was ist mit Verwendung stringund values()Aussage?

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

Ergebnismenge erreicht.

id  item
1   Hello
2   John
3   Smith

1
Ich habe Ihre Antwort verwendet, aber nicht funktioniert, aber ich habe geändert und dies hat mit Union alle funktioniert. Ich verwende SQL 2005
Angel

9

Ich verwende die Antwort von frederic, aber dies hat in SQL Server 2005 nicht funktioniert

Ich habe es geändert und ich benutze selectmit union allund es funktioniert

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

Und die Ergebnismenge ist:

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you

Das ist wirklich großartig, was ich je in SQL gesehen habe, es hat für meinen Job funktioniert und ich weiß das zu schätzen, danke!
Abdurrahman I.

Ich war sehr aufgeregt, als ich das sah, weil es so sauber und leicht zu verstehen war, aber leider kann man es wegen der nicht in eine UDF stecken EXEC. EXECRuft implizit eine gespeicherte Prozedur auf, und Sie können gespeicherte Prozeduren in UDFs nicht verwenden.
Kristen Hammack

Das funktioniert perfekt !! Ich wollte eine Funktion (SplitStrings_Moden) von hier aus verwenden: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments , die dies tut, und es dauerte anderthalb Minuten, um die Daten aufzuteilen und zurückzukehren die Zeilen, wenn nur 4 Kontonummern verwendet werden. Ich habe Ihre Version mit einem Links-Join in der Tabelle mit den Daten zu den Kontonummern getestet und es dauerte ungefähr 2 oder 3 Sekunden! Großer Unterschied und funktioniert einwandfrei! Ich würde diese 20 Stimmen geben, wenn möglich!
MattE

8

Dieses Muster funktioniert gut und Sie können verallgemeinern

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

Beachten Sie FELD , INDEX und TYP .

Lassen Sie eine Tabelle mit Bezeichnern wie

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

Dann können Sie schreiben

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

Teilen und Gießen aller Teile.


Dies ist die einzige Lösung, mit der Sie bestimmte Typen umwandeln können und die mäßig effizient ist (CLR ist immer noch am effizientesten, aber dieser Ansatz verarbeitet eine 8-GB-, 10-Token- und 10-Millionen-Zeilentabelle in etwa 9 Minuten (aws m3-Server, 4k iops) bereitgestellte Fahrt)
Andrew Hill

7

Wenn Ihre Datenbank eine Kompatibilitätsstufe von 130 oder höher aufweist, können Sie die Funktion STRING_SPLIT zusammen mit den Klauseln OFFSET FETCH verwenden , um das bestimmte Element nach Index abzurufen .

Um den Artikel auf Index N (basierend auf Null) zu erhalten, können Sie den folgenden Code verwenden

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

Führen Sie den folgenden Code aus, um die Kompatibilitätsstufe Ihrer Datenbank zu überprüfen :

SELECT compatibility_level  
FROM sys.databases WHERE name = 'YourDBName';

Der Trick ist in den OFFSET 1-REIHEN, die das erste Element überspringen und das zweite Element zurückgeben. Wenn Ihre Indizes auf 0 basieren und @X die Variable ist, die den Elementindex enthält, den Sie abrufen möchten, können Sie OFFSET @X ROWS
Gorgi Rankovski am

Okay, habe dies vorher nicht verwendet ... xmlGut zu wissen ... Ich würde immer noch den auf -split basierenden Ansatz bevorzugen , da er das Abrufen des Werttyps sicher ermöglicht und keine Unterabfrage benötigt, aber dies ist eine gut. +1 von meiner Seite
Shnugo

3
Das Problem hierbei ist, dass STRING_SPLIT die Reihenfolge der zurückgegebenen Ergebnisse nicht garantiert. Ihr Artikel 1 kann also mein Artikel 1 sein oder auch nicht.
user1443098

@GorgiRankovski, Verwenden von STRING_SPLITAnforderungen für v2016 +. In diesem Fall ist es viel besser, OPENJSONoder zu verwenden JSON_VALUE. Vielleicht möchten Sie meine Antwort überprüfen
Shnugo

6

Ich habe im Internet nach der Lösung gesucht und das Folgende funktioniert für mich. Ref .

Und Sie rufen die Funktion folgendermaßen auf:

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

Mit dieser Funktion können Sie nicht einfach auf das N-te Element zugreifen.
Björn Lindqvist

6

Noch ein weiterer Teil des Strings per Delimeter:

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

und die Verwendung:

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

was zurückgibt:

c

Ich mag diese Lösung als Option, um einen einzelnen Teilstring zurückzugeben, anstatt eine analysierte Tabelle zu erhalten, aus der Sie dann auswählen müssen. Die Verwendung eines Tabellenergebnisses hat seine Verwendung, aber für das, was ich brauchte, funktionierte dies perfekt.
James H

5

Versuche dies:

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

Testen Sie es so:

select * from SplitWordList('Hello John Smith')

Ich habe es durchgemacht und es ist perfekt wie das, was ich will! Ich kann es auch anpassen, um von mir gewählte Sonderzeichen zu ignorieren!
Vikas

5

Im folgenden Beispiel wird ein rekursiver CTE verwendet

Update 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 auf SQLFiddle


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

2

Sie können eine Zeichenfolge in SQL teilen, ohne eine Funktion zu benötigen:

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

Wenn Sie beliebige Zeichenfolgen unterstützen müssen (mit XML-Sonderzeichen)

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

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

1

Ich weiß, dass es eine alte Frage ist, aber ich denke, jemand kann von meiner Lösung profitieren.

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

Vorteile:

  • Es trennt alle 3 Teilzeichenfolgen-Trennzeichen durch ''.
  • Man darf die while-Schleife nicht verwenden, da dies die Leistung verringert.
  • Sie müssen nicht schwenken, da alle resultierenden Unterzeichenfolgen in einer Zeile angezeigt werden

Einschränkungen:

  • Man muss die Gesamtzahl kennen. von Leerzeichen (Teilzeichenfolge).

Hinweis : Die Lösung kann Teilzeichenfolgen bis zu N geben.

Um die Einschränkung zu überwinden, können wir die folgende Referenz verwenden .

Aber auch hier kann die obige Lösung nicht in einer Tabelle verwendet werden (Actaully konnte ich sie nicht verwenden).

Wieder hoffe ich, dass diese Lösung jemandem helfen kann.

Update: Bei Datensätzen> 50000 ist die Verwendung nicht ratsam , LOOPSda dies die Leistung beeinträchtigt


1

Reine satzbasierte Lösung TVFmit rekursiv CTE. Sie können JOINund APPLYdiese Funktion zu jedem Datensatz.

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

Verwendungszweck:

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

Ergebnis:

value   index
-------------
John    1

1

Fast alle anderen Antworten ersetzen die zu teilende Zeichenfolge, wodurch CPU-Zyklen verschwendet werden und unnötige Speicherzuweisungen durchgeführt werden.

Ich beschreibe hier einen viel besseren Weg, um einen String-Split durchzuführen: http://www.digitalruby.com/split-string-sql-server/

Hier ist der Code:

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.

0

Rekursive CTE-Lösung mit Serverschmerzen, testen Sie sie

MS SQL Server 2008 Schema-Setup :

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

Abfrage 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

Ergebnisse :

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |

0

Während ich der XML-basierten Antwort von Josejuan ähnlich war, stellte ich fest, dass die Verarbeitung des XML-Pfads nur einmal und das Schwenken mäßig effizienter war:

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

lief in 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

lief in 9:20


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

UND BENUTZEN SIE ES

select *from dbo.fnSplitString('Querying SQL Server','')

0

Wenn jemand nur einen Teil des Textes erhalten möchte, kann er dies verwenden

Wählen Sie * aus 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

Ich habe das entwickelt,

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

Die einzige Aufmerksamkeit, die Sie beachten sollten, ist Punkt '.' Das Ende des @x sollte immer da sein.


0

Aufbauend auf der @ NotingsImpossible-Lösung oder besser gesagt auf der am meisten bewerteten Antwort (knapp unter der akzeptierten) stellte ich fest, dass die folgende schnelle und schmutzige Lösung meine eigenen Anforderungen erfüllt - sie hat den Vorteil, dass sie ausschließlich innerhalb der SQL-Domäne liegt.

Wenn ich eine Zeichenfolge "erste; zweite; dritte; vierte; fünfte" gebe, möchte ich beispielsweise die dritte Zeichenfolge erhalten. Dies funktioniert nur, wenn wir wissen, wie viele Token die Zeichenfolge haben wird - in diesem Fall ist es 5. Meine Vorgehensweise besteht also darin, die letzten beiden Token wegzuschneiden (innere Abfrage) und dann die ersten beiden Token wegzuschneiden (innere Abfrage). äußere Abfrage)

Ich weiß, dass dies hässlich ist und die spezifischen Bedingungen abdeckt, in denen ich war, aber ich poste es nur für den Fall, dass jemand es nützlich findet. Prost

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

Dies funktioniert nur, wenn wir wissen, wie viele Token die Zeichenfolge haben wird - eine brechende Einschränkung ...
Shnugo

0
declare @strng varchar(max)='hello john smith'
select (
    substring(
        @strng,
        charindex(' ', @strng) + 1,
        (
          (charindex(' ', @strng, charindex(' ', @strng) + 1))
          - charindex(' ',@strng)
        )
    ))

0

Ab SQL Server 2016 haben wir string_split

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

SELECT value FROM string_split(@string, ',')

Das ist gut und schön, aber es geht nicht um die Frage, ob das n-te Ergebnis erzielt werden soll.
Johnie Karr

STRING_SPLITgarantiert nicht die Rücksendung der gleichen Bestellung. Aber OPENJSONtut (siehe meine Antwort (Update-Abschnitt) )
Shnugo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.