sql >> Base de Datos >  >> RDS >> Sqlserver

Usando T-SQL, devuelve el enésimo elemento delimitado de una cadena

Esta es la respuesta más fácil para recuperar el 67 (type-safe!! ):

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

A continuación, encontrará ejemplos de cómo usar esto con variables para la cadena, el delimitador y la posición (incluso para casos extremos con caracteres prohibidos por XML)

La fácil

Esta pregunta no se trata de un enfoque de división de cadenas , sino sobre cómo obtener el elemento n . La forma más fácil y completamente en línea sería esta OMI:

Esta es una una línea real para obtener la parte 2 delimitada por un espacio:

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

Las variables se pueden usar con sql:variable() o sql:column()

Por supuesto puedes usar variables para delimitador y posición (use sql:column para recuperar la posición directamente del valor de una consulta):

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 con caracteres prohibidos por XML

Si su cadena puede incluir caracteres prohibidos , todavía puedes hacerlo de esta manera. Simplemente use FOR XML PATH en su cadena primero para reemplazar implícitamente todos los caracteres prohibidos con la secuencia de escape adecuada.

Es un caso muy especial si, además, su delimitador es el punto y coma . En este caso, reemplazo el delimitador primero por '#DLMT#', y finalmente lo reemplazo por las etiquetas 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)');

ACTUALIZACIÓN para SQL-Server 2016+

Lamentablemente, los desarrolladores olvidaron devolver el índice de la parte con STRING_SPLIT . Pero, usando SQL-Server 2016+, hay JSON_VALUE y OPENJSON .

Con JSON_VALUE podemos pasar la posición como matriz de índice.

Para OPENJSON la documentación establece claramente:

Cuando OPENJSON analiza una matriz JSON, la función devuelve los índices de los elementos en el texto JSON como claves.

Una cadena como 1,2,3 no necesita nada más que corchetes:[1,2,3] .
Una cadena de palabras como this is an example debe ser ["this","is","an"," example"] .
Estas son operaciones de cadena muy fáciles. Solo pruébalo:

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

--Vea esto para un divisor de cadenas seguro de posición (basado en cero ):

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

En esta publicación, probé varios enfoques y descubrí que OPENJSON es realmente rápido Incluso mucho más rápido que el famoso método "delimitedSplit8k()"...

ACTUALIZACIÓN 2:obtenga los valores con seguridad de tipos

Podemos usar una matriz dentro de una matriz simplemente usando [[]] duplicado . Esto permite escribir WITH -cláusula:

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