sql >> Database teknologi >  >> RDS >> Sqlserver

Brug T-SQL til at returnere n'te separerede element fra en streng

Dette er det nemmeste svar til at fjerne 67 (typesikkert!! ):

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

I det følgende finder du eksempler på, hvordan du bruger dette med variabler for strengen, afgrænseren og positionen (selv for kant-cases med XML-forbudte tegn)

Den nemme

Dette spørgsmål handler ikke om en strengopdelt tilgang , men om hvordan man får det n'te element . Den nemmeste, fuldt inlineable måde ville være denne IMO:

Dette er en rigtig one-liner for at få del 2 afgrænset af et mellemrum:

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 bruges med sql:variable() eller sql:column()

Selvfølgelig du kan bruge variabler for afgrænsning og position (brug sql:column for at hente positionen direkte fra en forespørgsels værdi):

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-forbudte tegn

Hvis din streng muligvis indeholder forbudte tegn , du kan stadig gøre det på denne måde. Brug bare FOR XML PATH på din streng først for at erstatte alle forbudte tegn med den passende escape-sekvens implicit.

Det er et meget specielt tilfælde, hvis - derudover - din afgrænsning er semikolon . I dette tilfælde erstatter jeg afgrænsningstegnet først til '#DLMT#', og erstatter dette til XML-tags til sidst:

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

OPDATERING til SQL-Server 2016+

Desværre glemte udviklerne at returnere delens indeks med STRING_SPLIT . Men ved brug af SQL-Server 2016+ er der JSON_VALUE og OPENJSON .

Med JSON_VALUE vi kan passere i positionen som indeksets array.

Til OPENJSON dokumentationen siger tydeligt:

Når OPENJSON parser et JSON-array, returnerer funktionen indekserne for elementerne i JSON-teksten som nøgler.

En streng som 1,2,3 behøver intet mere end parenteser:[1,2,3] .
En række ord som this is an example skal være ["this","is","an"," example"] .
Dette er meget nemme strengoperationer. Bare prøv det:

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 dette for en positionssikker streng-splitter (nul-baseret ):

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

I dette indlæg testede jeg forskellige tilgange og fandt ud af, at OPENJSON er virkelig hurtig. Endnu meget hurtigere end den berømte "delimitedSplit8k()"-metode...

OPDATERING 2 - Få værdierne typesikker

Vi kan bruge et array i et array simpelthen ved at bruge fordoblet [[]] . Dette giver mulighed for en indtastet 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. Left Outer Join returnerer ikke alle rækker fra mit venstre bord?

  2. Rails, MySQL og Snow Leopard

  3. Tilføj tidsstempelkolonne med standard NOW() kun for nye rækker

  4. Hvordan kan jeg indsætte JSON-objekt i Postgres ved hjælp af Java readyStatement?