sql >> Databasteknik >  >> RDS >> Sqlserver

Använd T-SQL, returnera n:te avgränsade element från en sträng

Detta är det enklaste svaret för att få 67:an (typsäker!! ):

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

I det följande hittar du exempel på hur du använder detta med variabler för strängen, avgränsaren och positionen (även för kant-case med XML-förbjudna tecken)

Den enkla

Den här frågan handlar inte om en strängdelning , men om hur man får det n:te elementet . Det enklaste, helt inlineable sättet skulle vara detta IMO:

Det här är en riktig one-liner för att få del 2 avgränsad av ett mellanslag:

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

Variabler kan användas med sql:variable() eller sql:column()

Naturligtvis kan du använda variabler för avgränsare och position (använd sql:column för att hämta positionen direkt från en frågas värde):

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 med XML-förbjudna tecken

Om din sträng kan innehålla förbjudna tecken , du kan fortfarande göra det på det här sättet. Använd bara FOR XML PATH på din sträng först för att ersätta alla förbjudna tecken med den passande escape-sekvensen implicit.

Det är ett mycket speciellt fall om - dessutom - din avgränsare är semikolon . I det här fallet byter jag först ut avgränsaren till '#DLMT#' och ersätter detta till XML-taggarna till sist:

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

UPPDATERING för SQL-Server 2016+

Tyvärr glömde utvecklarna att returnera delens index med STRING_SPLIT . Men med SQL-Server 2016+ finns det JSON_VALUE och OPENJSON .

Med JSON_VALUE vi kan skicka in positionen som indexets array.

För OPENJSON dokumentationen anger tydligt:

När OPENJSON analyserar en JSON-array returnerar funktionen indexen för elementen i JSON-texten som nycklar.

En sträng som 1,2,3 behöver inget mer än hakparenteser:[1,2,3] .
En sträng med ord som this is an example måste vara ["this","is","an"," example"] .
Detta är mycket enkla strängoperationer. Testa bara:

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

--Se detta för en positionssäker strängdelare (nollbaserad ):

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

I det här inlägget testade jag olika tillvägagångssätt och fann att OPENJSON är riktigt snabb. Till och med mycket snabbare än den berömda "delimitedSplit8k()"-metoden...

UPPDATERING 2 - Få värdena typsäker

Vi kan använda en array inom en array helt enkelt genom att använda fördubblad [[]] . Detta tillåter en inskriven WITH -klausul:

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


  1. PHP + SQL Server - Hur ställer man in teckenuppsättning för anslutning?

  2. MySQL:finns inte i GROUP BY

  3. Hur man hittar de databassamlingar som stöds av din SQL Server-instans

  4. Hur FIELD() fungerar i MariaDB