Wie teile ich eine Zeichenfolge auf, damit ich auf Element x zugreifen kann?

29 answers
  • Ich glaube nicht, dass SQL Server über eine integrierte Split-Funktion verfügt. Abgesehen von einer UDF ist die einzige andere Antwort, die ich kenne, das Hijacking der Funktion PARSENAME:

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

    PARSENAME nimmt einen String und teilt ihn auf das Punktzeichen auf. Als zweites Argument wird eine Zahl verwendet, die angibt, welches Segment der Zeichenfolge zurückgegeben werden soll (von hinten nach vorne).

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

    Offensichtliches Problem ist, wenn die Zeichenfolge bereits einen Punkt enthält. Ich denke immer noch, dass eine UDF der beste Weg ist ... irgendwelche anderen Vorschläge?

    02 November 2015
    Luke Girvinuser1452962
  • Sie finden die Lösung unter Vom SQL-Benutzer definierte Funktion für Parse eine durch Trennzeichen getrennte Zeichenfolge hilfreich (aus The Code Project ).

    Sie können diese einfache Logik verwenden:

     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
     
    02 December 2016
    Nhan
  • Erstellen Sie zunächst eine Funktion (mit CTE macht der Ausdruck "Common Table" keine temporäre Tabelle erforderlich)

      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
     

    Verwenden Sie diese dann als beliebige Tabelle (oder ändern Sie sie so, dass sie in Ihre vorhandene gespeicherte Prozedur passt).

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

    Aktualisieren

    Die vorherige Version würde für eine Eingabezeichenfolge mit mehr als 4000 Zeichen fehlschlagen. Diese Version berücksichtigt die Einschränkung:

     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
     

    Die Verwendung bleibt gleich.

    07 July 2016
    vzczc
  • Die meisten Lösungen verwenden hier Schleifen oder rekursive CTEs. Ich verspreche Ihnen, dass ein satzbasierter Ansatz überlegen ist:

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

    Mehr zu Splitfunktionen, warum (und Beweis) das), während Schleifen und rekursive CTEs nicht skaliert werden, und bessere Alternativen, wenn Zeichenfolgen, die von der Anwendungsebene kommen, geteilt werden:

    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

    12 November 2013
    Aaron Bertrand
  • Sie können eine Zahlentabelle verwenden, um die Zeichenfolge zu analysieren.

    Erstellen Sie eine physikalische Zahlentabelle:

         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
     

    Erstellen Sie einen Test Tabelle mit 1000000 Zeilen

         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
     

    Erstellen Sie die Funktion

         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
     

    Verwendung (gibt 3 Mil-Zeilen in 40s auf meinem Laptop aus)

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

    Bereinigung

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

    Die Leistung hier ist nicht erstaunlich, sondern das Aufrufen einer Funktion über eine Million Zeilentabelle ist nicht die beste idee. Wenn Sie einen über viele Zeilen verteilten String ausführen, würde ich die Funktion vermeiden.

    28 October 2016
    Nathan SkerlSleeplessKnight
  • Hier ist eine UDF, die das erledigt. Es gibt eine Tabelle mit den begrenzten Werten zurück, Sie haben nicht alle Szenarien ausprobiert, aber Ihr Beispiel funktioniert gut.

     
    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
     

    Man würde es so nennen:

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

    Bearbeiten: Aktualisierte Lösung zur Behandlung von Trennzeichen mit einer Länge & gt; 1 wie in:

     
    select * From SplitString('Hello**John**Smith','**')
     
    05 August 2008
    brendan
  • Kein Code, aber lesen Sie dazu den endgültigen Artikel. Alle -Lösungen in anderen Antworten sind Varianten der in diesem Artikel aufgeführten: Arrays and Listen in SQL Server 2005 und darüber hinaus

    Persönlich habe ich meistens eine Numbers-Tabellenlösung verwendet, weil sie zu dem passt, was ich tun muss ...

    26 September 2010
    gbn
  • Hier poste ich eine einfache Lösung:

     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 INTO @t(val)
              SELECT  r.value('.','varchar(MAX)') as item
              FROM  @xml.nodes('/t') as records(r)
              RETURN
            END
     


    Führen Sie die Funktion wie folgt aus

       select * from dbo.split('Hello John Smith',' ')
     
    03 April 2013
    Mudassir Hasan
  • Bei dieser Frage geht es nicht um einen String-Split-Ansatz , sondern um das n-te Element .

    Alle Antworten hier führen eine Art Zeichenfolgenaufteilung mit Rekursion durch, CTE s, mehrere CHARINDEX, REVERSE und PATINDEX Methoden, Zahlentabellen, CROSS APPLY s ... Die meisten Antworten beziehen sich auf viele Codezeilen.

    Aber - wenn Sie wirklich nur einen Ansatz wollen, um die n-tes Element - Dies kann als echter Einzeiler , keine UDF, nicht einmal als Unterauswahl ausgeführt werden ... Und als zusätzlicher Vorteil: sicherer Typ

    Lasse Teil 2 durch ein Leerzeichen getrennt:

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

    Natürlich können Sie Variablen als Trennzeichen und Position verwenden (verwenden Sie sql:column, um die Position direkt vom Wert einer Abfrage abzurufen):

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

    Wenn Ihre Zeichenfolge möglicherweise verbotene Zeichen enthält (insbesondere eine unter &><), können Sie dies immer noch tun. Verwenden Sie einfach FOR XML PATH für Ihre Zeichenfolge, um alle verbotenen Zeichen implizit mit der passenden Escape-Sequenz zu ersetzen.

    Es ist ein sehr spezieller Fall, wenn - zusätzlich - Ihr Trennzeichen ist Semikolon . In diesem Fall ersetze ich zuerst das Trennzeichen durch '# DLMT #' und ersetze dies schließlich durch die XML-Tags:

     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)');
     
    06 March 2018
    ShnugoGateKiller
  • Meiner Meinung nach macht ihr es zu kompliziert. Erstellen Sie einfach eine CLR-UDF und erledigen Sie sie.

     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()));
      }
    };
     
    20 July 2012
    Damon Drake