Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Geben Sie mit T-SQL das n-te durch Trennzeichen getrennte Element aus einer Zeichenfolge zurück

Dies ist die einfachste Antwort, um die 67 (typsicher!!) wiederherzustellen ):

SELECT CAST('<x>' + REPLACE('1,222,2,67,888,1111',',','</x><x>') + '</x>' AS XML).value('/x[4]','int')

Im Folgenden finden Sie Beispiele, wie Sie dies mit Variablen für die Zeichenfolge, das Trennzeichen und die Position verwenden können (auch für Grenzfälle mit XML-verbotenen Zeichen)

Der Einfache

Bei dieser Frage geht es nicht um einen String-Split-Ansatz , sondern darüber, wie man das n-te Element erhält . Der einfachste, vollständig inlinefähige Weg wäre dieser IMO:

Dies ist ein echter Einzeiler um Teil 2 durch ein Leerzeichen getrennt zu bekommen:

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

Variablen können mit sql:variable() verwendet werden oder sql:column()

Natürlich können Sie Variablen verwenden für Trennzeichen und Position (verwenden Sie sql:column 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)')

Edge-Case mit XML-verbotenen Zeichen

Wenn Ihre Zeichenfolge verbotene Zeichen enthalten könnte , du kannst es immer noch so machen. Verwenden Sie einfach FOR XML PATH auf Ihrem String, 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 STRING_SPLIT zurückzugeben . Bei Verwendung von SQL-Server 2016+ gibt es jedoch JSON_VALUE und OPENJSON .

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

Für OPENJSON In der Dokumentation heißt es eindeutig:

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

Eine Zeichenfolge wie 1,2,3 braucht nichts weiter als Klammern:[1,2,3] .
Eine Zeichenfolge wie this is an example muss ["this","is","an"," example"] sein .
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,']'));

--Siehe dies für einen positionssicheren String-Splitter (nullbasiert ):

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 OPENJSON ist wirklich schnell. Sogar viel schneller als die berühmte "delimitedSplit8k()"-Methode...

UPDATE 2 - Werte typsicher erhalten

Wir können ein Array innerhalb eines Arrays verwenden einfach durch doppeltes [[]] . Dies ermöglicht einen getippten WITH -Klausel:

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