Home > SQL-Server 2005 > T-SQL Stringhandling (Extraktion von Token)

T-SQL Stringhandling (Extraktion von Token)

Heute möchte ich einmal ein kleines Script bzw. eine kleine Funktion vorstellen die zum Zerlegen von Strings sehr nützlich sein kann.

Wenn man strukturierte Texte vorliegen hat und daraus bestimmte Teile extrahieren soll, so kann man dies sicher mit den Stringfunktionen von T-SQL erledigen. Gerade die Verarbeitung von Listen (Kommasepariert oder sonst irgendwie) bedingt aber die wiederholte Extraktion von Textblöcken (Token) die durch verschiedenste Begrenzungszeichen voneinander getrennt sind.

Als Token verstehe ich in diesem Zusammenhang Teile eines Textes die durch Begrenzer (Delimiter) voneinander logisch getrennt werden. In einem Satz wären das die Worte, in einer Liste die durch z.B. Komma getrennten Artikelnummern oder einfach Bestandteile eines Tabellennamens usw.

Sehen wir uns hierzu mal ein paar Beispiele an:

  • String: ‘Hello World, how are you?’
  • String: ‘123,2345,231234,3456′
  • String: ‘[AdventureWorks].[Person]. [Contact]‘

Extrahieren wir mal das dritte Wort aus Beispiel 1, den zweiten Parameterwert aus Beispiel 2 und den Tabellennamen aus Beispiel 3!

Mit Standardmitteln sicherlich ebenfalls zu realisieren, jedoch mit einer Funktion viel eleganter und Übersichtlicher zu gestalten:

 T-SQL |  copy code |? 
01
DECLARE @String1 VARCHAR(1000)
02
DECLARE @String2 VARCHAR(1000)
03
DECLARE @String3 VARCHAR(1000)
04
 
05
-- --------------------------------------------------------------------
06
-- Single Delimiter ' ' Leerzeichen
07
-- --------------------------------------------------------------------
08
 
09
SET @String1 = 'Hello World, how are you?'
10
 
11
PRINT @String1 + '  ==>  ' + dbo.fn_GetToken(' ',3,@String)
12
 
13
-- --------------------------------------------------------------------
14
-- Single Delimiter ',' Komma
15
-- --------------------------------------------------------------------
16
 
17
SET @String2 = '123,2345,231234,3456'
18
 
19
PRINT @String2 + '  ==>  ' + dbo.fn_GetToken(',',2,@String)
20
 
21
-- --------------------------------------------------------------------
22
-- Paariger Delimiter []
23
-- --------------------------------------------------------------------
24
 
25
SET @String3 = '[AdventureWorks].[Person].<leerzeichen>[Contact]'
26
 
27
PRINT @String3 + '  ==>  ' + dbo.fn_GetToken('[<-*->]',3,@String)

Die Syntax für den Fuktionsaufruf sieht dann wie folgt aus:

dbo.fn_GetToken(single_delimiter|delimiter_pair, position, source_string)

  • single_delimiter: Beliebige Zeichenkette die Token voneinander trennt.
  • delimiter_pair: Beliebige Zeichenkette, danach ein Trennblock <-*-> und dann wieder eine Zeichenkette.
  • position: Das n-te Token beginnend mit 1.
    Wenn position = 0 dann wird der String unverändert returniert.
  • source_string: Der Quellstring.
  • Als Rückgabewert wird das Token ohne die Delimiter geliefert oder NULL im Fehlerfall.

… und hier wie immer das Script dazu:

 T-SQL |  copy code |? 
001
CREATE FUNCTION dbo.[fn_GetToken]
002
(
003
    @Delim NVARCHAR(100),
004
    @TokenNr INT,
005
    @Zeile NVARCHAR(MAX)
006
)
007
RETURNS NVARCHAR(MAX)
008
AS
009
BEGIN
010
 
011
    DECLARE @RetWert        NVARCHAR(MAX)
012
    DECLARE @Line           NVARCHAR(MAX)	
013
 
014
    DECLARE @End_Delim      NVARCHAR(100)
015
    DECLARE @End_Delim_Len  INT			
016
 
017
    DECLARE @Delim_Pos_S    INT
018
    DECLARE @Delim_Pos_E    INT			
019
 
020
    DECLARE @Delim_S NVARCHAR(100)
021
    DECLARE @Delim_E NVARCHAR(100)	
022
 
023
    DECLARE @Pos        INT
024
    DECLARE @LEN        INT
025
    DECLARE @Delim_Len  INT
026
    DECLARE @TokenCnt   INT		
027
 
028
    -- Initialisierung der Parameter auf Defaults
029
 
030
    IF @Zeile   IS NULL SET @Zeile   = N''
031
    IF @Delim   IS NULL SET @Delim   = N';'
032
    IF @TokenNr IS NULL SET @TokenNr = 1
033
 
034
    SET @Line = @Zeile
035
 
036
    -- Anfangs- und Ende-Delimiter bestimmen wenn die Zeichenfolge
037
    -- '<-*->' im Delimiter vorhanden ist.
038
    -- Im anderen Fall wird die Zeichenfolge als Einzeldelimiter verwendet.
039
 
040
    IF CHARINDEX(N'<-*->',@Delim) > 0
041
    BEGIN
042
        SET @End_Delim = SUBSTRING(@Delim,
043
                         CHARINDEX(N'<-*->',@Delim)
044
                         +5,datalength(@Delim)/2)
045
 
046
        SET @Delim     = SUBSTRING(@Delim,1,
047
                         CHARINDEX(N'<-*->',@Delim)-1)
048
    END
049
    ELSE
050
    BEGIN
051
        SET @End_Delim = @Delim
052
    END
053
 
054
    -- Die Länge des Begrenzers muss halbiert werden (Unicode)
055
 
056
    SET @Delim_Len     = datalength(@Delim)/2
057
    SET @End_Delim_Len = datalength(@End_Delim)/2
058
 
059
    -- Start und Ende setzen. Wenn nur ein Begrenzer definiert ist,
060
    -- dann sind Start und Ende gleich.
061
 
062
    SET @Delim_S = @Delim
063
    SET @Delim_E = @End_Delim
064
 
065
    -- Die eckige Klammer muss als regulärer Ausdruck gecastet werden.
066
    -- Aus [ wird [[]
067
 
068
    SET @Delim     = REPLACE(@Delim    ,N'[','[[]')
069
    SET @End_Delim = REPLACE(@End_Delim,N'[','[[]')
070
 
071
    -- Wenn es nur einen Delimiter gibt, muss die ganze Zeile auf eine
072
    -- Zwei-Delimiter-Logik erweitert werden
073
    -- Dies bedeutet, dass die Zeile von einem Start und Enddelimiter
074
    -- umschlossen sein muss. Diese Werte werden ggf. hinzugefügt.
075
 
076
    IF @Delim_S = @Delim_E
077
    BEGIN
078
        IF SUBSTRING(@Zeile,1,@Delim_Len    ) <> @Delim_S
079
            SET @Zeile = @Delim_S + @Zeile
080
 
081
        IF RIGHT    (@Zeile  ,@End_Delim_Len) <> @Delim_E
082
            SET @Zeile = @Zeile + @Delim_E
083
    END
084
 
085
    -- Delimiter erweitern 
086
 
087
    IF SUBSTRING(@Delim,1,1) <> N'%' SET @Delim = N'%'+ @Delim
088
    IF RIGHT    (@Delim,1)   <> N'%' SET @Delim = @Delim + N'%'
089
 
090
    IF SUBSTRING(@End_Delim,1,1) <> N'%' SET @End_Delim = N'%'+ @End_Delim
091
    IF RIGHT    (@End_Delim,1)   <> N'%' SET @End_Delim = @End_Delim + N'%'
092
 
093
    -- Initial-Position setzen
094
 
095
    SET @Pos       = 1
096
    SET @TokenCnt  = 1
097
 
098
    SET @Delim_Pos_S = PATINDEX(@Delim,@Zeile)
099
    SET @Delim_Pos_E = PATINDEX(@End_Delim,
100
                            SUBSTRING(@Zeile,@Delim_Pos_S+@Delim_Len ,
101
                            datalength(@Zeile)/2))
102
                     + @Delim_Pos_S
103
                     + @Delim_Len-1
104
 
105
    -- Delimiter und Token suchen
106
 
107
    IF @TokenNr > 0
108
    BEGIN
109
        WHILE (@Delim_Pos_S > 0 and @Delim_Pos_E > 0)
110
        BEGIN
111
 
112
            -- Ermittlung des Token ...
113
 
114
            SET @LEN     = @Delim_Pos_E - @Delim_Pos_S - @Delim_Len
115
            SET @RetWert = SUBSTRING(@Zeile,@Delim_Pos_S + @Delim_Len,@LEN)
116
 
117
            -- Vorbereitung auf den nächsten Token ...
118
 
119
            IF @TokenCnt = @TokenNr BREAK ELSE SET @TokenCnt = @TokenCnt + 1;
120
 
121
            SET @Zeile       = SUBSTRING(@Zeile,@Delim_Pos_E + @End_Delim_Len,
122
                                                datalength(@Zeile)/2 - @Delim_Pos_E)
123
 
124
            IF @Delim_S = @Delim_E and datalength(@Zeile)/2 > 0
125
            BEGIN
126
                IF SUBSTRING(@Zeile,1,@Delim_Len    ) <> @Delim_S
127
                    SET @Zeile = @Delim_S + @Zeile
128
 
129
                IF RIGHT    (@Zeile  ,@End_Delim_Len) <> @Delim_E
130
                    SET @Zeile = @Zeile + @Delim_E
131
            END
132
 
133
            SET @Delim_Pos_S = PATINDEX(@Delim,@Zeile)
134
 
135
            SET @Delim_Pos_E = PATINDEX(@End_Delim,
136
                                   SUBSTRING(@Zeile,@Delim_Pos_S+@Delim_Len,
137
                                   datalength(@Zeile)/2))
138
                             + @Delim_Pos_S
139
                             + @Delim_Len-1
140
 
141
            IF @Delim_Pos_E <= @Delim_Len SET @RetWert = NULL
142
        END
143
    END
144
 
145
    -- Returncodes setzen
146
 
147
    SET @RetWert = CASE WHEN @TokenCnt <  @TokenNr	THEN NULL
148
                        WHEN @TokenNr  <  0			THEN NULL
149
                        WHEN @TokenNr  =  0			THEN @Line
150
                        ELSE @RetWert
151
                   END
152
 
153
    RETURN @RetWert
154
END
155

Tip: Man kann die Funktionsaufrufe natürlich auch ineinander verschachteln!

have fun

 

KategorienSQL-Server 2005
  1. Bisher keine Kommentare
  1. Bisher keine Trackbacks