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