Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

À l'aide de T-SQL, renvoie le nième élément délimité d'une chaîne

C'est la réponse la plus simple pour récupérer le 67 (type-safe !! ):

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

Dans ce qui suit, vous trouverez des exemples d'utilisation avec des variables pour la chaîne, le délimiteur et la position (même pour les cas extrêmes avec des caractères XML interdits)

La plus facile

Cette question ne concerne pas une approche de division de chaîne , mais sur comment obtenir le nième élément . Le moyen le plus simple et entièrement inline serait ce IMO :

C'est un véritable one-liner pour obtenir la partie 2 délimitée par un espace :

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

Les variables peuvent être utilisées avec sql:variable() ou sql:column()

Bien sûr, vous pouvez utiliser des variables pour le délimiteur et la position (utilisez sql:column pour récupérer la position directement à partir de la valeur d'une requête) :

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 avec des caractères XML interdits

Si votre chaîne peut inclure des caractères interdits , vous pouvez toujours le faire de cette façon. Utilisez simplement FOR XML PATH sur votre chaîne pour remplacer implicitement tous les caractères interdits par la séquence d'échappement appropriée.

C'est un cas très particulier si - en plus - votre délimiteur est le point-virgule . Dans ce cas, je remplace d'abord le délimiteur par '#DLMT#', et je remplace enfin celui-ci par les balises XML :

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

MISE À JOUR pour SQL-Server 2016+

Malheureusement, les développeurs ont oublié de renvoyer l'index de la pièce avec STRING_SPLIT . Mais, en utilisant SQL-Server 2016+, il y a JSON_VALUE et OPENJSON .

Avec JSON_VALUE on peut passer en position comme tableau d'index.

Pour OPENJSON la documentation indique clairement :

Lorsque OPENJSON analyse un tableau JSON, la fonction renvoie les index des éléments du texte JSON sous forme de clés.

Une chaîne comme 1,2,3 n'a besoin que de crochets :[1,2,3] .
Une chaîne de mots comme this is an example doit être ["this","is","an"," example"] .
Ce sont des opérations très simples sur les chaînes. Essayez-le :

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

--Voir ceci pour un séparateur de chaîne sécurisé (basé sur zéro ):

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

Dans ce post, j'ai testé diverses approches et j'ai trouvé que OPENJSON est vraiment rapide. Encore bien plus rapide que la fameuse méthode "delimitedSplit8k()"...

MISE À JOUR 2 – Récupérer les valeurs de type sécurisé

Nous pouvons utiliser un tableau dans un tableau simplement en utilisant [[]] doublé . Cela permet un WITH typé -clause :

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