¿Cómo puedo dividir una cadena para poder acceder al elemento x?

votos
442

Usando SQL Server, ¿cómo puedo dividir una cadena para poder acceder al elemento x?

Tome una cadena Hola John Smith. ¿Cómo puedo dividir la cadena por espacio y acceder al ítem en el índice 1 que debería devolver Juan?

Publicado el 05/08/2008 a las 19:15
fuente por usuario
En otros idiomas...                            


46 respuestas

votos
335

No creo que SQL Server tenga una función de división integrada, por lo que, aparte de una UDF, la única otra respuesta que conozco es para secuestrar la función PARSENAME:

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2) 

PARSENAME toma una cadena y la divide en el carácter del período. Toma un número como su segundo argumento, y ese número especifica qué segmento de la cadena devolver (trabajando de atrás hacia adelante).

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3)  --return Hello

El problema obvio es cuando la cadena ya contiene un punto. Sigo pensando que usar una UDF es la mejor manera ... ¿Alguna otra sugerencia?

Respondida el 05/08/2008 a las 19:45
fuente por usuario

votos
177

Puede encontrar la solución en Función definida por el usuario de SQL para analizar una cadena delimitada útil (de The Code Project ).

Puedes usar esta simple lógica:

Declare @products varchar(200) = '1|20|3|343|44|6|8765'
Declare @individual varchar(20) = null

WHILE LEN(@products) > 0
BEGIN
    IF PATINDEX('%|%', @products) > 0
    BEGIN
        SET @individual = SUBSTRING(@products,
                                    0,
                                    PATINDEX('%|%', @products))
        SELECT @individual

        SET @products = SUBSTRING(@products,
                                  LEN(@individual + '|') + 1,
                                  LEN(@products))
    END
    ELSE
    BEGIN
        SET @individual = @products
        SET @products = NULL
        SELECT @individual
    END
END
Respondida el 05/08/2008 a las 19:28
fuente por usuario

votos
106

Primero, cree una función (usando CTE, la expresión de tabla común elimina la necesidad de una tabla temporal)

 create function dbo.SplitString 
    (
        @str nvarchar(4000), 
        @separator char(1)
    )
    returns table
    AS
    return (
        with tokens(p, a, b) AS (
            select 
                1, 
                1, 
                charindex(@separator, @str)
            union all
            select
                p + 1, 
                b + 1, 
                charindex(@separator, @str, b + 1)
            from tokens
            where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
      )
    GO

Luego, úselo como cualquier tabla (o modifíquelo para que se ajuste a su proceso almacenado existente) de esta manera.

select s 
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1

Actualizar

La versión anterior fallaría para cadenas de entrada de más de 4000 caracteres. Esta versión se ocupa de la limitación:

create function dbo.SplitString 
(
    @str nvarchar(max), 
    @separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
    select 
        cast(1 as bigint), 
        cast(1 as bigint), 
        charindex(@separator, @str)
    union all
    select
        p + 1, 
        b + 1, 
        charindex(@separator, @str, b + 1)
    from tokens
    where b > 0
)
select
    p-1 ItemIndex,
    substring(
        @str, 
        a, 
        case when b > 0 then b-a ELSE LEN(@str) end) 
    AS s
from tokens
);

GO

El uso sigue siendo el mismo

Respondida el 05/08/2008 a las 19:57
fuente por usuario

votos
51

La mayoría de las soluciones aquí Utilice mientras bucles o CTE recursivas. Un enfoque basado en el conjunto será superior, lo prometo:

CREATE FUNCTION [dbo].[SplitString]
    (
        @List NVARCHAR(MAX),
        @Delim VARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN ( SELECT [Value] FROM 
          ( 
            SELECT 
              [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
              CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
            FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
              FROM sys.all_objects) AS x
              WHERE Number <= LEN(@List)
              AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
          ) AS y
        );

Más en dividir funciones, por eso (y prueba de que), mientras que los bucles y las CTE recursivas no escalan, y mejores alternativas, si división de cadenas procedentes de la capa de aplicación:

http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings

http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql

http://sqlblog.com/blogs/aaron_bertrand/archive/2010/07/07/splitting-a-list-of-integers-another-roundup.aspx

Respondida el 12/11/2013 a las 18:16
fuente por usuario

votos
37

Puede aprovechar una tabla de números para realizar el análisis de cadenas.

Crea una tabla de números físicos:

    create table dbo.Numbers (N int primary key);
    insert into dbo.Numbers
        select top 1000 row_number() over(order by number) from master..spt_values
    go

Crear tabla de prueba con 1000000 filas

    create table #yak (i int identity(1,1) primary key, array varchar(50))

    insert into #yak(array)
        select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn
    go

Crea la función

    create function [dbo].[ufn_ParseArray]
        (   @Input      nvarchar(4000), 
            @Delimiter  char(1) = ',',
            @BaseIdent  int
        )
    returns table as
    return  
        (   select  row_number() over (order by n asc) + (@BaseIdent - 1) [i],
                    substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s
            from    dbo.Numbers
            where   n <= convert(int, len(@Input)) and
                    substring(@Delimiter + @Input, n, 1) = @Delimiter
        )
    go

Uso (salidas 3mil filas en 40s en mi computadora portátil)

    select * 
    from #yak 
    cross apply dbo.ufn_ParseArray(array, ',', 1)

limpiar

    drop table dbo.Numbers;
    drop function  [dbo].[ufn_ParseArray]

El rendimiento aquí no es sorprendente, pero llamar a una función de más de un millón de filas no es la mejor idea. Si realizara una cadena dividida en muchas filas, evitaría la función.

Respondida el 27/10/2008 a las 17:48
fuente por usuario

votos
20

Aquí hay un UDF que lo hará. Devolverá una tabla de los valores delimitados, no ha probado todos los escenarios pero su ejemplo funciona bien.


CREATE FUNCTION SplitString 
(
    -- Add the parameters for the function here
    @myString varchar(500),
    @deliminator varchar(10)
)
RETURNS 
@ReturnTable TABLE 
(
    -- Add the column definitions for the TABLE variable here
    [id] [int] IDENTITY(1,1) NOT NULL,
    [part] [varchar](50) NULL
)
AS
BEGIN
        Declare @iSpaces int
        Declare @part varchar(50)

        --initialize spaces
        Select @iSpaces = charindex(@deliminator,@myString,0)
        While @iSpaces > 0

        Begin
            Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))

            Insert Into @ReturnTable(part)
            Select @part

    Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))


            Select @iSpaces = charindex(@deliminator,@myString,0)
        end

        If len(@myString) > 0
            Insert Into @ReturnTable
            Select @myString

    RETURN 
END
GO

Lo llamarías así:


Select * From SplitString('Hello John Smith',' ')

Editar: solución actualizada para manejar delimitadores con una len> 1 como en:


select * From SplitString('Hello**John**Smith','**')
Respondida el 05/08/2008 a las 19:39
fuente por usuario

votos
16

Sin código, pero leyó el artículo definitivo sobre esto. Todas las soluciones en otras respuestas son sabores de los que se enumeran en este artículo: matrices y listas de SQL Server 2005 y más allá

En lo personal, he utilizado una solución tabla Números más a menudo porque se adapta a lo que tengo que hacer ...

Respondida el 26/09/2010 a las 14:44
fuente por usuario

votos
15

Aquí dejo una forma sencilla de solución

CREATE FUNCTION [dbo].[split](
          @delimited NVARCHAR(MAX),
          @delimiter NVARCHAR(100)
        ) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
        AS
        BEGIN
          DECLARE @xml XML
          SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'

          INSERT IGNORE  INTO @t(val)
          SELECT  r.value('.','varchar(MAX)') as item
          FROM  @xml.nodes('/t') as records(r)
          RETURN
        END


Ejecutar la función como esta

  select * from dbo.split('Hello John Smith',' ')
Respondida el 30/01/2013 a las 10:41
fuente por usuario

votos
12

Esta pregunta es no se trata de un enfoque de división de cadena , sino sobre cómo obtener el enésimo elemento .

Todas las respuestas aquí están haciendo algún tipo de división cadena utilizando la recursividad, CTEs, múltiple CHARINDEX, REVERSEy PATINDEX, inventando funciones, requieren métodos de CLR, tablas de números, CROSS APPLYs ... La mayoría de las respuestas cubren muchas líneas de código.

Pero - si realmente quiere nada más que un enfoque para obtener el elemento número n - esto se puede hacer tan real, de una sola línea , sin UDF, ni siquiera un sub-select ... Y como un beneficio extra: un tipo seguro

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

Por supuesto se pueden utilizar variables para delimitador y la posición (utiliza sql:columnpara 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)')

Si la cadena podría incluir caracteres prohibidos (especialmente uno entre &><), todavía puede hacerlo de esta manera. Sólo tiene que utilizar FOR XML PATHen su primera cadena para reemplazar todos los caracteres prohibidos con la secuencia de escape apropiado de manera implícita.

Es un caso muy especial si - además - el delimitador es el punto y coma . En este caso se sustituye la primera delimitador a '# # DLMT', y reemplazar esta a las etiquetas XML finalmente:

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)');
Respondida el 08/07/2016 a las 17:41
fuente por usuario

votos
10

Qué acerca del uso stringy values()estado de cuenta?

DECLARE @str varchar(max)
SET @str = 'Hello John Smith'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited TABLE(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, '''),(''')
SET @str = 'SELECT * FROM (VALUES(''' + @str + ''')) AS V(A)' 

INSERT IGNORE  INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Conjunto de resultados alcanzados.

id  item
1   Hello
2   John
3   Smith
Respondida el 01/03/2013 a las 17:26
fuente por usuario

votos
10

En mi opinión que ustedes están haciendo es demasiado complicado. Basta con crear una UDF CLR y hacerse con él.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

public partial class UserDefinedFunctions {
  [SqlFunction]
  public static SqlString SearchString(string Search) {
    List<string> SearchWords = new List<string>();
    foreach (string s in Search.Split(new char[] { ' ' })) {
      if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) {
        SearchWords.Add(s);
      }
    }

    return new SqlString(string.Join(" OR ", SearchWords.ToArray()));
  }
};
Respondida el 19/07/2012 a las 22:46
fuente por usuario

votos
8

Este patrón funciona bien y se puede generalizar

Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
                          ^^^^^                                   ^^^^^     ^^^^

notar CAMPO , INDICE y TIPO .

Dejar un poco de mesa con identificadores como

sys.message.1234.warning.A45
sys.message.1235.error.O98
....

Entonces, se puede escribir

SELECT Source         = q.value('(/n[1])', 'varchar(10)'),
       RecordType     = q.value('(/n[2])', 'varchar(20)'),
       RecordNumber   = q.value('(/n[3])', 'int'),
       Status         = q.value('(/n[4])', 'varchar(5)')
FROM   (
         SELECT   q = Convert(xml,'<n>'+Replace(fieldName,'.','</n><n>')+'</n>')
         FROM     some_TABLE
       ) Q

la división y la fundición de todas las partes.

Respondida el 11/11/2014 a las 14:31
fuente por usuario

votos
8

Yo uso la respuesta de Frederic, pero esto no funciona en SQL Server 2005

He modificado y estoy usando selectcon union ally funciona

DECLARE @str varchar(max)
SET @str = 'Hello John Smith how are you'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''')
SET @str = ' SELECT  ''' + @str + '''  ' 

INSERT IGNORE  INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Y el conjunto de resultados es:

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you
Respondida el 13/08/2013 a las 16:11
fuente por usuario

votos
6

Sin embargo, otra parte obtener enésimo de la cadena por la función delimitador:

create function GetStringPartByDelimeter (
    @value as nvarchar(max),
    @delimeter as nvarchar(max),
    @position as int
) returns NVARCHAR(MAX) 
AS BEGIN
    declare @startPos as int
    declare @endPos as int
    set @endPos = -1
    while (@position > 0 and @endPos != 0) begin
        set @startPos = @endPos + 1
        set @endPos = charindex(@delimeter, @value, @startPos)

        if(@position = 1) begin
            if(@endPos = 0)
                set @endPos = len(@value) + 1

            return substring(@value, @startPos, @endPos - @startPos)
        end

        set @position = @position - 1
    end

    return null
end

y el uso de:

select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)

que devuelve:

c
Respondida el 08/01/2016 a las 11:30
fuente por usuario

votos
6

Yo estaba buscando la solución en la red y las obras por debajo de mí. Ref .

Y se llama a la función como esta:

SELECT * FROM dbo.split('ram shyam hari gopal',' ')

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1))       
RETURNS @temptable TABLE (items VARCHAR(8000))       
AS       
BEGIN       
    DECLARE @idx INT       
    DECLARE @slice VARCHAR(8000)        
    SELECT @idx = 1       
    IF len(@String)<1 OR @String IS NULL  RETURN       
    WHILE @idx!= 0       
    BEGIN       
        SET @idx = charindex(@Delimiter,@String)       
        IF @idx!=0       
            SET @slice = LEFT(@String,@idx - 1)       
        ELSE       
            SET @slice = @String       
        IF(len(@slice)>0)  
            INSERT IGNORE  INTO @temptable(Items) VALUES(@slice)       
        SET @String = RIGHT(@String,len(@String) - @idx)       
        IF len(@String) = 0 break       
    END   
    RETURN       
END
Respondida el 20/11/2011 a las 07:40
fuente por usuario

votos
5

El ejemplo siguiente utiliza un CTE recursiva

actualización 18/09/2013

CREATE FUNCTION dbo.SplitStrings_CTE(@List nvarchar(max), @Delimiter nvarchar(1))
RETURNS @returns TABLE (val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level]))
AS
BEGIN
;WITH cte AS
 (
  SELECT SUBSTRING(@List, 0, CHARINDEX(@Delimiter,  @List + @Delimiter)) AS val,
         CAST(STUFF(@List + @Delimiter, 1, CHARINDEX(@Delimiter, @List + @Delimiter), '') AS nvarchar(max)) AS stval, 
         1 AS [level]
  UNION ALL
  SELECT SUBSTRING(stval, 0, CHARINDEX(@Delimiter, stval)),
         CAST(STUFF(stval, 1, CHARINDEX(@Delimiter, stval), '') AS nvarchar(max)),
         [level] + 1
  FROM cte
  WHERE stval != ''
  )
  INSERT IGNORE  @returns
  SELECT REPLACE(val, ' ','' ) AS val, [level]
  FROM cte
  WHERE val > ''
  RETURN
END

Demo en SQLFiddle

Respondida el 14/03/2013 a las 11:18
fuente por usuario

votos
5

Prueba esto:

CREATE function [SplitWordList]
(
 @list varchar(8000)
)
returns @t table 
(
 Word varchar(50) not null,
 Position int identity(1,1) not null
)
as begin
  declare 
    @pos int,
    @lpos int,
    @item varchar(100),
    @ignore varchar(100),
    @dl int,
    @a1 int,
    @a2 int,
    @z1 int,
    @z2 int,
    @n1 int,
    @n2 int,
    @c varchar(1),
    @a smallint
  select 
    @a1 = ascii('a'),
    @a2 = ascii('A'),
    @z1 = ascii('z'),
    @z2 = ascii('Z'),
    @n1 = ascii('0'),
    @n2 = ascii('9')
  set @ignore = '''"'
  set @pos = 1
  set @dl = datalength(@list)
  set @lpos = 1
  set @item = ''
  while (@pos <= @dl) begin
    set @c = substring(@list, @pos, 1)
    if (@ignore not like '%' + @c + '%') begin
      set @a = ascii(@c)
      if ((@a >= @a1) and (@a <= @z1))  
        or ((@a >= @a2) and (@a <= @z2))
        or ((@a >= @n1) and (@a <= @n2))
      begin
        set @item = @item + @c
      end else if (@item > '') begin
        insert into @t values (@item)
        set @item = ''
      end
    end 
    set @pos = @pos + 1
  end
  if (@item > '') begin
    insert into @t values (@item)
  end
  return
end

Pruébalo así:

select * from SplitWordList('Hello John Smith')
Respondida el 05/08/2008 a las 19:41
fuente por usuario

votos
3


    Alter Function dbo.fn_Split
    (
    @Expression nvarchar(max),
    @Delimiter  nvarchar(20) = ',',
    @Qualifier  char(1) = Null
    )
    RETURNS @Results TABLE (id int IDENTITY(1,1), value nvarchar(max))
    AS
    BEGIN
       /* USAGE
            Select * From dbo.fn_Split('apple pear grape banana orange honeydew cantalope 3 2 1 4', ' ', Null)
            Select * From dbo.fn_Split('1,abc,"Doe, John",4', ',', '"')
            Select * From dbo.fn_Split('Hello 0,"&""&&&&', ',', '"')
       */

       -- Declare Variables
       DECLARE
          @X     xml,
          @Temp  nvarchar(max),
          @Temp2 nvarchar(max),
          @Start int,
          @End   int

       -- HTML Encode @Expression
       Select @Expression = (Select @Expression For XML Path(''))

       -- Find all occurences of @Delimiter within @Qualifier and replace with |||***|||
       While PATINDEX('%' + @Qualifier + '%', @Expression) > 0 AND Len(IsNull(@Qualifier, '')) > 0
       BEGIN
          Select
             -- Starting character position of @Qualifier
             @Start = PATINDEX('%' + @Qualifier + '%', @Expression),
             -- @Expression starting at the @Start position
             @Temp = SubString(@Expression, @Start + 1, LEN(@Expression)-@Start+1),
             -- Next position of @Qualifier within @Expression
             @End = PATINDEX('%' + @Qualifier + '%', @Temp) - 1,
             -- The part of Expression found between the @Qualifiers
             @Temp2 = Case When @End < 0 Then @Temp Else Left(@Temp, @End) End,
             -- New @Expression
             @Expression = REPLACE(@Expression,
                                   @Qualifier + @Temp2 + Case When @End < 0 Then '' Else @Qualifier End,
                                   Replace(@Temp2, @Delimiter, '|||***|||')
                           )
       END

       -- Replace all occurences of @Delimiter within @Expression with '</fn_Split><fn_Split>'
       -- And convert it to XML so we can select from it
       SET
          @X = Cast('<fn_Split>' +
                    Replace(@Expression, @Delimiter, '</fn_Split><fn_Split>') +
                    '</fn_Split>' as xml)

       -- Insert into our returnable table replacing '|||***|||' back to @Delimiter
       INSERT IGNORE  @Results
       SELECT
          "Value" = LTRIM(RTrim(Replace(C.value('.', 'nvarchar(max)'), '|||***|||', @Delimiter)))
       FROM
          @X.nodes('fn_Split') as X(C)

       -- Return our temp table
       RETURN
    END

Respondida el 05/11/2013 a las 01:12
fuente por usuario

votos
2

Si su base de datos tiene nivel de compatibilidad de 130 o superior, puede utilizar la STRING_SPLIT función junto con OFFSET FETCH cláusulas para obtener el objeto específico por el índice.

Para obtener el elemento en la posición 1, puede utilizar el siguiente código

SELECT value
FROM STRING_SPLIT('Hello John Smith',' ')
ORDER BY (SELECT NULL)
OFFSET 1 ROWS
FETCH NEXT 1 ROWS ONLY

Para comprobar el nivel de compatibilidad de la base de datos , ejecutar este código:

SELECT compatibility_level  
FROM sys.databases WHERE name = 'YourDBName';
Respondida el 05/04/2018 a las 07:23
fuente por usuario

votos
2

Puede dividir una cadena en SQL sin necesidad de una función:

DECLARE @bla varchar(MAX)
SET @bla = 'BED40DFC-F468-46DD-8017-00EF2FA3E4A4,64B59FC5-3F4D-4B0E-9A48-01F3D4F220B0,A611A108-97CA-42F3-A2E1-057165339719,E72D95EA-578F-45FC-88E5-075F66FD726C'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'varchar(36)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE(@bla, ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol);

Si tiene que apoyar cadenas arbitrarias (con caracteres especiales XML)

DECLARE @bla NVARCHAR(MAX)
SET @bla = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>,Barnes & Noble,abc,def,ghi'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'nvarchar(MAX)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE((SELECT @bla FOR XML PATH('')), ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol); 
Respondida el 23/10/2015 a las 07:07
fuente por usuario

votos
2

Casi todas las otras respuestas de código dividida están reemplazando la cadena que se divide el cual desperdicia ciclos de CPU y realiza las asignaciones de memoria innecesarias.

Cubro una manera mucho mejor que hacer una escisión de cadena aquí: http://www.digitalruby.com/split-string-sql-server/

Aquí está el código:

SET NOCOUNT ON

-- You will want to change nvarchar(MAX) to nvarchar(50), varchar(50) or whatever matches exactly with the string column you will be searching against
DECLARE @SplitStringTable TABLE (Value nvarchar(MAX) NOT NULL)
DECLARE @StringToSplit nvarchar(MAX) = 'your|string|to|split|here'
DECLARE @SplitEndPos int
DECLARE @SplitValue nvarchar(MAX)
DECLARE @SplitDelim nvarchar(1) = '|'
DECLARE @SplitStartPos int = 1

SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)

WHILE @SplitEndPos > 0
BEGIN
    SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, (@SplitEndPos - @SplitStartPos))
    INSERT IGNORE  @SplitStringTable (Value) VALUES (@SplitValue)
    SET @SplitStartPos = @SplitEndPos + 1
    SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)
END

SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, 2147483647)
INSERT IGNORE  @SplitStringTable (Value) VALUES(@SplitValue)

SET NOCOUNT OFF

-- You can select or join with the values in @SplitStringTable at this point.
Respondida el 26/08/2014 a las 17:50
fuente por usuario

votos
2

Sé que es una vieja pregunta, pero yo creo que alguien puede beneficiarse de mi solución.

select 
SUBSTRING(column_name,1,CHARINDEX(' ',column_name,1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,1
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)+1
    ,LEN(column_name))
from table_name

SQL FIDDLE

ventajas:

  • Se separa todo el 3 sub-cadenas deliminator por ''.
  • No hay que utilizar el bucle while, ya que disminuye el rendimiento.
  • No hay necesidad de Pivotee como todos los sub-cadena resultante se mostrará en una fila

limitaciones:

  • Uno debe saber el nº total. de los espacios (sub-string).

Nota : la solución puede dar subcadena hasta a N.

Buscando superar la limitación que podemos utilizar el siguiente ref .

Pero, de nuevo por encima de la solución no puede ser el uso de una tabla (Actaully yo no era capaz de usarlo).

Una vez más espero que esta solución puede ayudar a algunos-uno.

Actualización: En caso de registros> 50000 no es aconsejable utilizar LOOPS, ya que degradará el rendimiento

Respondida el 24/01/2013 a las 07:43
fuente por usuario

votos
1

Sé que es tarde, pero recientemente he tenido este requisito y se acercó con el código de abajo. Yo no tengo la opción de utilizar la función definida por el usuario. Espero que esto ayude.

SELECT 
    SUBSTRING(
                SUBSTRING('Hello John Smith' ,0,CHARINDEX(' ','Hello John Smith',CHARINDEX(' ','Hello John Smith')+1)
                        ),CHARINDEX(' ','Hello John Smith'),LEN('Hello John Smith')
            )
Respondida el 17/09/2018 a las 21:07
fuente por usuario

votos
1

Una solución simple para analizar NOMBRE Y APELLIDO

DECLARE @Name varchar(10) = 'John Smith'

-- Get First Name
SELECT SUBSTRING(@Name, 0, (SELECT CHARINDEX(' ', @Name)))

-- Get Last Name
SELECT SUBSTRING(@Name, (SELECT CHARINDEX(' ', @Name)) + 1, LEN(@Name))

En mi caso (y en muchos otros parece ...), tengo una lista de nombres y apellidos separados por un solo espacio. Esto se puede utilizar directamente en el interior de una instrucción de selección para analizar el nombre y apellido.

-- i.e. Get First and Last Name from a table of Full Names
SELECT SUBSTRING(FullName, 0, (SELECT CHARINDEX(' ', FullName))) as FirstName,
SUBSTRING(FullName, (SELECT CHARINDEX(' ', FullName)) + 1, LEN(FullName)) as LastName,
From FullNameTable
Respondida el 20/08/2018 a las 18:59
fuente por usuario

votos
1

Aquí es una función que cumplir el objetivo de la cuestión de la división de una cadena y acceder artículo X:

CREATE FUNCTION [dbo].[SplitString]
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255),
   @ElementNumber INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN

       DECLARE @inp VARCHAR(MAX)
       SET @inp = (SELECT REPLACE(@List,@Delimiter,'_DELMTR_') FOR XML PATH(''))

       DECLARE @xml XML
       SET @xml = '<split><el>' + REPLACE(@inp,'_DELMTR_','</el><el>') + '</el></split>'

       DECLARE @ret VARCHAR(MAX)
       SET @ret = (SELECT
              el = split.el.value('.','varchar(max)')
       FROM  @xml.nodes('/split/el[string-length(.)>0][position() = sql:variable("@elementnumber")]') split(el))

       RETURN @ret

END

Uso:

SELECT dbo.SplitString('Hello John Smith', ' ', 2)

Resultado:

John
Respondida el 26/04/2018 a las 18:16
fuente por usuario

votos
1

La respuesta de Aaron Bertrand es grande, pero impreciso. No maneja con precisión un espacio como un delimitador (como era el ejemplo en la pregunta original), ya que las tiras de función longitud espacios finales.

El siguiente es el código, con un pequeño ajuste para permitir un delimitador de espacio:

CREATE FUNCTION [dbo].[SplitString]
(
    @List NVARCHAR(MAX),
    @Delim VARCHAR(255)
)
RETURNS TABLE
AS
    RETURN ( SELECT [Value] FROM 
      ( 
        SELECT 
          [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
          CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
        FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
          FROM sys.all_objects) AS x
          WHERE Number <= LEN(@List)
          AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim+'x')-1) = @Delim
      ) AS y
    );
Respondida el 22/03/2018 a las 11:38
fuente por usuario

votos
1

A partir de SQL Server 2016 que string_split

DECLARE @string varchar(100) = 'Richard, Mike, Mark'

SELECT value FROM string_split(@string, ',')
Respondida el 04/09/2017 a las 18:52
fuente por usuario

votos
1

Solución basada en el uso conjunto pura TVFcon recursiva CTE. Se puede JOINy APPLYesta función para cualquier conjunto de datos.

create function [dbo].[SplitStringToResultSet] (@value varchar(max), @separator char(1))
returns table
as return
with r as (
    select value, cast(null as varchar(max)) [x], -1 [no] from (select rtrim(cast(@value as varchar(max))) [value]) as j
    union all
    select right(value, len(value)-case charindex(@separator, value) when 0 then len(value) else charindex(@separator, value) end) [value]
    , left(r.[value], case charindex(@separator, r.value) when 0 then len(r.value) else abs(charindex(@separator, r.[value])-1) end ) [x]
    , [no] + 1 [no]
    from r where value > '')

select ltrim(x) [value], [no] [index] from r where x is not null;
go

Uso:

select *
from [dbo].[SplitStringToResultSet]('Hello John Smith', ' ')
where [index] = 1;

Resultado:

value   index
-------------
John    1
Respondida el 13/01/2015 a las 06:37
fuente por usuario

votos
0

A partir de SQL Server 2016 hay funciones para analizar los datos JSON. Para acceder a un elemento de un índice particular de una cadena delimitada, utilice la JSON_VALUEfunción. Adecuadamente se requiere de datos con formato JSON, sin embargo: las cadenas deben estar entre comillas dobles "y el delimitador debe ser una coma ,, con toda la cadena encerrada entre corchetes [].

DECLARE @SampleString NVARCHAR(MAX) = '"Hello John Smith"';
--Format as JSON data.
SET @SampleString = '[' + REPLACE(@SampleString, ' ', '","') + ']';
SELECT 
    JSON_VALUE(@SampleString, '$[0]') AS Element1Value,
    JSON_VALUE(@SampleString, '$[1]') AS Element2Value,
    JSON_VALUE(@SampleString, '$[2]') AS Element3Value;

Salida

Element1Value         Element2Value       Element3Value
--------------------- ------------------- ------------------------------
Hello                 John                Smith

(1 row affected)
Respondida el 07/05/2019 a las 05:54
fuente por usuario

votos
0

Éste se basa en cadena, de posición y Delimitador

CREAR fnx_splitstring FUNCIÓN (@stringToSplit VARCHAR (MAX), int @Position, char @SpecialChar (1))
RETURNS TABLE @returnList ([Nombre] [nvarchar] (500))
AS
COMIENZO
SET @stringToSplit = @stringToSplit + @SpecialChar
DECLARE @ nombrar NVARCHAR (255)
DECLARE @pos INT
DECLARE @i int
SET @i = 0
WHILE CHARINDEX (@SpecialChar, @stringToSplit)> 0
COMIENZO
SET @i = @i + 1
SELECT @pos = CHARINDEX (@SpecialChar, @stringToSplit)
@Name SELECT = SUBSTRING (@stringToSplit, 1, @ pos-1)
si @i = @Position
COMENZAR
INSERT INTO @returnList
SELECT @Name
RETURN
END
SELECT @stringToSplit = SUBSTRING (@stringToSplit, @ pos + 1, LEN (@stringToSplit ) - @ pos)
END
RETURN
END

Prueba de que como esto SELECT * desde fnx_splitstring ( 'V4686 / V4686-H-AW-60,25', 2', '-')

Respondida el 01/05/2019 a las 19:46
fuente por usuario

votos
0

Si las subseries no contienen duplicados, puede utilizar lo siguiente:

WITH testdata(string) AS (
    SELECT 'a' UNION ALL
    SELECT 'a b' UNION ALL
    SELECT 'a b c' UNION ALL
    SELECT 'a b c d'
)
SELECT *
FROM testdata
CROSS APPLY (
    SELECT value AS substring
         , ROW_NUMBER() OVER(ORDER BY CHARINDEX(' ' + value + ' ', ' ' + string + ' ')) AS n
    FROM STRING_SPLIT(string, ' ')
) AS substrings
WHERE n = 1

El STRING_SPLITgenera las subseries, pero no proporciona el índice de la subcadena. Se puede utilizar CHARINDEXpara generar el número de índice y va a ser correcta, siempre y cuando las subseries son únicos. Se producirá un error de a b b c, a b c c d e, etc.

Respondida el 27/01/2019 a las 15:27
fuente por usuario

votos
0

Puede utilizar STRING_SPLITlas funciones disponibles en SQL Server 2016 o posterior. Se informa de que no hay ninguna garantía de que las subseries se devolverán en ningún orden en particular.

WITH testdata(id, string) AS (
    SELECT 1, NULL UNION ALL
    SELECT 2, 'a' UNION ALL
    SELECT 3, 'a b' UNION ALL
    SELECT 4, 'a b c' UNION ALL
    SELECT 5, 'a b c d'
)
SELECT testdata.id, testdata.string, (
    SELECT value AS substr FROM STRING_SPLIT(testdata.string, ' ') FOR XML PATH(''), TYPE
).value('substr[2]', 'VARCHAR(100)') AS [2nd_substr]
FROM testdata

Dónde:

  • STRING_SPLIT devuelve una tabla con una columna llamada value
  • FOR XML PATH('') transforma a las filas <substr>a</substr><substr>b</substr>...
  • TYPEconvierte la de arriba para XMLDataType
  • value('substr[2]', 'VARCHAR(100)')corre expresión XPath en lo anterior y devuelve VARCHARel tipo de datos

Resultado:

| id | string  | 2nd_substr |
|----|---------|------------|
| 1  | NULL    | NULL       |
| 2  | a       | NULL       |
| 3  | a b     | b          |
| 4  | a b c   | b          |
| 5  | a b c d | b          |
Respondida el 24/01/2018 a las 09:27
fuente por usuario

votos
0

Un enfoque moderno utilizando STRING_SPLIT , requiere SQL Server 2016 y versiones posteriores.

DECLARE @string varchar(100) = 'Hello John Smith'

SELECT
    ROW_NUMBER() OVER (ORDER BY value) AS RowNr,
    value
FROM string_split(@string, ' ')

Resultado:

RowNr   value
1       Hello
2       John
3       Smith

Ahora es posible obtener ésimo elemento enésimo del número de fila.

Respondida el 02/01/2018 a las 12:02
fuente por usuario

votos
0

Sobre la solución @NothingsImpossible, o, más bien, hacer comentarios sobre la respuesta más votada (justo debajo de la aceptada uno), me encontré con el siguiente rápido y sucio- solución de cumplir mis propias necesidades - que tiene una ventaja de ser únicamente dentro del dominio de SQL.

dada una cadena "primero, en segundo lugar, en tercer lugar, en cuarto lugar, el quinto", por ejemplo, quiero conseguir el tercer símbolo. esto sólo funciona si sabemos cuántas fichas de la cadena va a tener - en este caso es 5. así que mi modo de acción es para cortar las dos últimas fichas de distancia (consulta interna), y luego a picar las dos primeras fichas de distancia ( consulta externa)

Yo sé que esto es feo y cubre las condiciones específicas en que estaba, pero estoy publicando por si acaso alguien lo encuentra útil. aclamaciones

select 
    REVERSE(
        SUBSTRING(
            reverse_substring, 
            0, 
            CHARINDEX(';', reverse_substring)
        )
    ) 
from 
(
    select 
        msg,
        SUBSTRING(
            REVERSE(msg), 
            CHARINDEX(
                ';', 
                REVERSE(msg), 
                CHARINDEX(
                    ';',
                    REVERSE(msg)
                )+1
            )+1,
            1000
        ) reverse_substring
    from 
    (
        select 'first;second;third;fourth;fifth' msg
    ) a
) b
Respondida el 31/10/2016 a las 11:18
fuente por usuario

votos
0
declare @strng varchar(max)='hello john smith'
select (
    substring(
        @strng,
        charindex(' ', @strng) + 1,
        (
          (charindex(' ', @strng, charindex(' ', @strng) + 1))
          - charindex(' ',@strng)
        )
    ))
Respondida el 14/07/2016 a las 02:29
fuente por usuario

votos
0

Me devoloped esto,

declare @x nvarchar(Max) = 'ali.veli.deli.';
declare @item nvarchar(Max);
declare @splitter char='.';

while CHARINDEX(@splitter,@x) != 0
begin
    set @item = LEFT(@x,CHARINDEX(@splitter,@x))
    set @x    = RIGHT(@x,len(@x)-len(@item) )
     select @item as item, @x as x;
end

la única atención que debiera es punto '' ese extremo de la @x se debe siempre estar allí.

Respondida el 15/10/2015 a las 07:50
fuente por usuario

votos
0

si alguien quiere obtener sólo una parte del texto seperatured puede utilizar este

seleccionar * de fromSplitStringSep ( 'Palabra1 wordr2 word3',' ')

CREATE function [dbo].[SplitStringSep] 
(
    @str nvarchar(4000), 
    @separator char(1)
)
returns table
AS
return (
    with tokens(p, a, b) AS (
        select 
        1, 
        1, 
        charindex(@separator, @str)
        union all
        select
            p + 1, 
            b + 1, 
            charindex(@separator, @str, b + 1)
        from tokens
        where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
  )
Respondida el 13/02/2015 a las 09:14
fuente por usuario

votos
0
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT IGNORE  INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

Y utilizarlo

select *from dbo.fnSplitString('Querying SQL Server','')
Respondida el 20/12/2014 a las 11:58
fuente por usuario

votos
0

mientras que es similar a la respuesta basado en XML por josejuan, he encontrado que el procesamiento de la vía xml sólo una vez, a continuación, pivotando fue moderadamente más eficiente:

select ID,
    [3] as PathProvidingID,
    [4] as PathProvider,
    [5] as ComponentProvidingID,
    [6] as ComponentProviding,
    [7] as InputRecievingID,
    [8] as InputRecieving,
    [9] as RowsPassed,
    [10] as InputRecieving2
    from
    (
    select id,message,d.* from sysssislog cross apply       ( 
          SELECT Item = y.i.value('(./text())[1]', 'varchar(200)'),
              row_number() over(order by y.i) as rn
          FROM 
          ( 
             SELECT x = CONVERT(XML, '<i>' + REPLACE(Message, ':', '</i><i>') + '</i>').query('.')
          ) AS a CROSS APPLY x.nodes('i') AS y(i)
       ) d
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as tokens 
    pivot 
    ( max(item) for [rn] in ([3],[4],[5],[6],[7],[8],[9],[10]) 
    ) as data

corrió en 08:30

select id,
tokens.value('(/n[3])', 'varchar(100)')as PathProvidingID,
tokens.value('(/n[4])', 'varchar(100)') as PathProvider,
tokens.value('(/n[5])', 'varchar(100)') as ComponentProvidingID,
tokens.value('(/n[6])', 'varchar(100)') as ComponentProviding,
tokens.value('(/n[7])', 'varchar(100)') as InputRecievingID,
tokens.value('(/n[8])', 'varchar(100)') as InputRecieving,
tokens.value('(/n[9])', 'varchar(100)') as RowsPassed
 from
(
    select id, Convert(xml,'<n>'+Replace(message,'.','</n><n>')+'</n>') tokens
         from sysssislog 
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as data

corrió en 09:20

Respondida el 08/12/2014 a las 03:59
fuente por usuario

votos
0

Solución CTE recursiva con el dolor servidor, probarlo

MS SQL Server 2008 Configuración de esquema :

create table Course( Courses varchar(100) );
insert into Course values ('Hello John Smith');

Consulta 1 :

with cte as
   ( select 
        left( Courses, charindex( ' ' , Courses) ) as a_l,
        cast( substring( Courses, 
                         charindex( ' ' , Courses) + 1 , 
                         len(Courses ) ) + ' ' 
              as varchar(100) )  as a_r,
        Courses as a,
        0 as n
     from Course t
    union all
      select 
        left(a_r, charindex( ' ' , a_r) ) as a_l,
        substring( a_r, charindex( ' ' , a_r) + 1 , len(a_R ) ) as a_r,
        cte.a,
        cte.n + 1 as n
    from Course t inner join cte 
         on t.Courses = cte.a and len( a_r ) > 0

   )
select a_l, n from cte
--where N = 1

resultados :

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |
Respondida el 16/01/2014 a las 11:38
fuente por usuario

votos
0

Esto es algo que hice con el fin de obtener un token específico en una cadena. (Probado en MSSQL 2008)

En primer lugar, la creación de las siguientes funciones: (que se encuentra en: aquí

CREATE FUNCTION dbo.SplitStrings_Moden
(
   @List NVARCHAR(MAX),
   @Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
  WITH E1(N)        AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
       E2(N)        AS (SELECT 1 FROM E1 a, E1 b),
       E4(N)        AS (SELECT 1 FROM E2 a, E2 b),
       E42(N)       AS (SELECT 1 FROM E4 a, E2 b),
       cteTally(N)  AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) 
                         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
       cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
                         WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
  SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
    FROM cteStart s;

y

create FUNCTION dbo.getToken
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255),
@Pos int
)
RETURNS varchar(max)
as 
begin
declare @returnValue varchar(max);
select @returnValue = tbl.Item from (
select ROW_NUMBER() over (order by (select null)) as id, * from dbo.SplitStrings_Moden(@List, @Delimiter)
) as tbl
where tbl.id = @Pos
return @returnValue
end

a continuación, se puede usar así:

select dbo.getToken('1111_2222_3333_', '_', 1)

que devuelven 1111

Respondida el 25/07/2013 a las 12:07
fuente por usuario

votos
0

Bueno, el mío no es tan sencillo, pero aquí está el código que uso para dividir una variable de entrada delimitada por comas en valores individuales, y ponerla en una variable de tabla. Estoy seguro de que podría modificar esto ligeramente para dividirlo en función de un espacio y luego hacer una consulta SELECCIONAR básica contra esa variable de tabla para obtener sus resultados.

-- Create temporary table to parse the list of accounting cycles.
DECLARE @tblAccountingCycles table
(
    AccountingCycle varchar(10)
)

DECLARE @vchAccountingCycle varchar(10)
DECLARE @intPosition int

SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ','
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)

IF REPLACE(@vchAccountingCycleIDs, ',', '') <> ''
BEGIN
    WHILE @intPosition > 0
    BEGIN
        SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1)))
        IF @vchAccountingCycle <> ''
        BEGIN
            INSERT IGNORE  INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle)
        END
        SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition)
        SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
    END
END

El concepto es más o menos lo mismo. Otra alternativa es aprovechar la compatibilidad con .NET dentro de SQL Server 2005. En esencia, puede escribir usted mismo un método simple en .NET que dividiría la cadena y luego la expondría como un procedimiento / función almacenada.

Respondida el 05/08/2008 a las 19:36
fuente por usuario

votos
-1

He estado usando la respuesta de vzczc usando CTE recursiva durante algún tiempo, pero he querido actualizarla para manejar un separador de longitud variable y también para manejar cadenas con los principales y menos "separadores", como cuando se tiene un archivo CSV con los registros como :

"Bob", "Smith", "Sunnyvale", "CA"

o cuando se trata de FQN de seis partes, tal como se muestra a continuación. Yo uso estos extensivamente para el registro de la subject_fqn para la auditoría, control de errores, etc, y ParseName sólo se ocupa de cuatro partes:

[netbios_name].[machine_name].[instance].[database].[schema].[table].[column]

Aquí está mi versión actualizada, y gracias a la vzczc para su puesto original!

select * from [utility].[split_string](N'"this"."string"."gets"."split"."and"."removes"."leading"."and"."trailing"."quotes"', N'"."', N'"', N'"');

select * from [utility].[split_string](N'"this"."string"."gets"."split"."but"."leaves"."leading"."and"."trailing"."quotes"', N'"."', null, null);

select * from [utility].[split_string](N'[netbios_name].[machine_name].[instance].[database].[schema].[table].[column]', N'].[', N'[', N']');

create function [utility].[split_string] ( 
  @input       [nvarchar](max) 
  , @separator [sysname] 
  , @lead      [sysname] 
  , @lag       [sysname]) 
returns @node_list table ( 
  [index]  [int] 
  , [node] [nvarchar](max)) 
  begin 
      declare @separator_length [int]= len(@separator) 
              , @lead_length    [int] = isnull(len(@lead), 0) 
              , @lag_length     [int] = isnull(len(@lag), 0); 
      -- 
      set @input = right(@input, len(@input) - @lead_length); 
      set @input = left(@input, len(@input) - @lag_length); 
      -- 
      with [splitter]([index], [starting_position], [start_location]) 
           as (select cast(@separator_length as [bigint]) 
                      , cast(1 as [bigint]) 
                      , charindex(@separator, @input) 
               union all 
               select [index] + 1 
                      , [start_location] + @separator_length 
                      , charindex(@separator, @input, [start_location] + @separator_length) 
               from   [splitter] 
               where  [start_location] > 0) 
      -- 
      insert into @node_list 
                  ([index],[node]) 
        select [index] - @separator_length                   as [index] 
               , substring(@input, [starting_position], case 
                                                            when [start_location] > 0 
                                                                then 
                                                              [start_location] - [starting_position] 
                                                            else 
                                                              len(@input) 
                                                        end) as [node] 
        from   [splitter]; 
      -- 
      return; 
  end; 
go 
Respondida el 19/08/2014 a las 20:45
fuente por usuario

votos
-1

Un simple algoritmo optimizado:

ALTER FUNCTION [dbo].[Split]( @Text NVARCHAR(200),@Splitor CHAR(1) )
RETURNS @Result TABLE ( value NVARCHAR(50)) 
AS
BEGIN
    DECLARE @PathInd INT
    Set @Text+=@Splitor
    WHILE LEN(@Text) > 0
    BEGIN
        SET @PathInd=PATINDEX('%'+@Splitor+'%',@Text)
        INSERT IGNORE  INTO  @Result VALUES(SUBSTRING(@Text, 0, @PathInd))
        SET @Text= SUBSTRING(@Text, @PathInd+1, LEN(@Text))
    END
        RETURN 
END
Respondida el 01/05/2014 a las 07:26
fuente por usuario

votos
-1

Aquí es una UDF de SQL que se pueden dividir una cadena y tomar sólo una determinada pieza.

create FUNCTION [dbo].[udf_SplitParseOut]
(
    @List nvarchar(MAX),
    @SplitOn nvarchar(5),
    @GetIndex smallint
)  
returns varchar(1000)
AS  

BEGIN

DECLARE @RtnValue table 
(

    Id int identity(0,1),
    Value nvarchar(MAX)
) 


    DECLARE @result varchar(1000)

    While (Charindex(@SplitOn,@List)>0)
    Begin
        Insert Into @RtnValue (value)
        Select Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
        Set @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
    End

    Insert Into @RtnValue (Value)
    Select Value = ltrim(rtrim(@List))

    select @result = value from @RtnValue where ID = @GetIndex

    Return @result
END
Respondida el 20/03/2014 a las 15:41
fuente por usuario

votos
-1

Aquí está mi solución que puede ayudar a alguien. La modificación de la respuesta de Jonesinator anteriormente.

Si tengo una serie de valores INT delimitados y quiero una mesa de INT devuelto (que luego pueda unir sucesivamente). por ejemplo, '1,20,3,343,44,6,8765'

Crear un archivo UDF:

IF OBJECT_ID(N'dbo.ufn_GetIntTableFromDelimitedList', N'TF') IS NOT NULL
    DROP FUNCTION dbo.[ufn_GetIntTableFromDelimitedList];
GO

CREATE FUNCTION dbo.[ufn_GetIntTableFromDelimitedList](@String NVARCHAR(MAX),                 @Delimiter CHAR(1))

RETURNS @table TABLE 
(
    Value INT NOT NULL
)
AS 
BEGIN
DECLARE @Pattern NVARCHAR(3)
SET @Pattern = '%' + @Delimiter + '%'
DECLARE @Value NVARCHAR(MAX)

WHILE LEN(@String) > 0
    BEGIN
        IF PATINDEX(@Pattern, @String) > 0
        BEGIN
            SET @Value = SUBSTRING(@String, 0, PATINDEX(@Pattern, @String))
            INSERT IGNORE  INTO @table (Value) VALUES (@Value)

            SET @String = SUBSTRING(@String, LEN(@Value + @Delimiter) + 1, LEN(@String))
        END
        ELSE
        BEGIN
            -- Just the one value.
            INSERT IGNORE  INTO @table (Value) VALUES (@String)
            RETURN
        END
    END

RETURN
END
GO

A continuación, obtener los resultados de la tabla:

SELECT * FROM dbo.[ufn_GetIntTableFromDelimitedList]('1,20,3,343,44,6,8765', ',')

1
20
3
343
44
6
8765

Y en una instrucción de combinación:

SELECT [ID], [FirstName]
FROM [User] u
JOIN dbo.[ufn_GetIntTableFromDelimitedList]('1,20,3,343,44,6,8765', ',') t ON u.[ID] = t.[Value]

1    Elvis
20   Karen
3    David
343  Simon
44   Raj
6    Mike
8765 Richard

Si desea obtener una lista de NVARCHARs en lugar de intercepciones a continuación, sólo cambiar la definición de tabla:

RETURNS @table TABLE 
(
    Value NVARCHAR(MAX) NOT NULL
)
Respondida el 20/06/2013 a las 00:42
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more