{"id":436,"date":"2007-01-30T22:32:41","date_gmt":"2007-01-30T21:32:41","guid":{"rendered":"http:\/\/www.glorf.it\/blog\/2007\/01\/30\/sql-talk\/sql-strings-zerhacken"},"modified":"2007-01-30T22:32:41","modified_gmt":"2007-01-30T21:32:41","slug":"sql-strings-zerhacken","status":"publish","type":"post","link":"http:\/\/www.glorf.it\/blog\/2007\/01\/30\/sql-talk\/sql-strings-zerhacken","title":{"rendered":"SQL: Strings zerhacken"},"content":{"rendered":"<p>Gestern kam mein Kollege Alexander mit einer ganz spannenden SQL-Fragestellung zu mir. weil die Anwendung auch noch den SQL-Server-2000 unterst&#252;tzt, mussten wir etwas in die Trickkiste greifen. Ich habe die Fragestellung jetzt daheim noch mal unter dem SQL-Server-2005 nachvollzogen und konnte dabei ein paar der schicken, neuen Features nutzen&#8230;<\/p>\n<p>Die Frage ist, wie man Strings, die &#252;ber eine Legacy-Schnittstelle kommen und so in die Datenbank geschrieben wurden, aufteilen kann. Beispielsweise soll die Zeichenfolge &quot;1234#12#bla#blub#132&quot; mit SQL anhand des Trenners &quot;#&quot; in die Einzelbestandteile aufgeteilt und dann weiterverarbeitet werden.<\/p>\n<p>So k&#246;nnte die Tabelle aussehen:<\/p>\n<table border=\"1\">\n<tr>\n<td><strong>LfdNr<\/strong><\/td>\n<td><strong>Zeichenkette<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>13242#22#blabla#233242#blubblub#12321#1312#1131##13123<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>13332#12#blibla#233242#blubblub#12321#1312#1131##13123<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>13353#21#blabli#233242#blubblub#12321#1312#1131##13123<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>13242#12#blibli#233242#blubblub#12321#1312#1131##13123<\/td>\n<\/tr>\n<tr>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<\/tr>\n<\/table>\n<p>Eine L&#246;sung, die mir pers&#246;nlich besonders gut gef&#228;llt, hat sich der gro&#223;e <a href=\"http:\/\/www.sql.co.il\/\">Itzik Ben Gan<\/a> ausgedacht: Dabei wird eine Hilfstabelle ben&#246;tigt, die nur eine Spalte hat, und f&#252;r jede Zahl einen Datensatz enth&#228;lt.<\/p>\n<table border=\"1\">\n<tr>\n<td><strong>n<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td>&#8230;<\/td>\n<\/tr>\n<\/table>\n<p>Dann kann man mit einem Cross-Join zwischen den Tabellen in der Where-Klausel diejenigen S&#228;tze rausfischen, die an der n-ten Position ein &quot;#&quot; haben:<\/p>\n<p><code lang=\"sql\">...<br \/>\n\tfrom LegacyInput<br \/>\n\tcross join hilfstabelle<br \/>\n\twhere substring(&#x0027;#'+Zeichenkette+&#x0027;#', n, 1)=&#x0027;#&#x0027;<br \/>\n\t  and n <= len(Zeichenkette)+2<\/code><\/p>\n<p>Damit das erste Teilst&#252;ck bei 1 anf&#228;ngt, wird noch ein &quot;#&quot; vor den Anfang angef&#252;gt. Au&#223;erdem m&#252;ssen nur die Datens&#228;tze untersucht werden, die kleiner\/gleich der L&#228;nge der Zeichenkette sind. Das ist eigentlich doppelt gemoppelt und kann daher auch entfallen.<\/p>\n<p>Im SELECT werden dann die Einzelbestandteile rausgel&#246;st: von n bis zum n&#228;chsten &quot;#&quot;, wobei &quot;n&quot; eine Position ist, an der der ein &quot;#&quot; gefunden wurde.<\/p>\n<p><code lang=\"sql\">substring('#'+Zeichenkette, n+1, charindex('#',Zeichenkette+'#',n)-n)<\/code><\/p>\n<p>Das gesamte Statement sieht dann so aus:<\/p>\n<p><code lang=\"sql\">select\tlfdNr,<br \/>\n\t\tn-len(replace(substring(&#x0027;#'+Zeichenkette, 1, n), &#x0027;#&#x0027;, &#x0027;&#x0027;)) as &quot;TeilNr&quot;,<br \/>\n\t\tsubstring(&#x0027;#'+Zeichenkette, n+1, charindex(&#x0027;#',Zeichenkette+&#x0027;#',n)-n) as &quot;Teilst&#252;ck&quot;,<br \/>\n\t\tZeichenkette<br \/>\n\tfrom LegacyInput<br \/>\n\tcross join hilfstabelle<br \/>\n\twhere substring(&#x0027;#'+Zeichenkette, n, 1)=&#x0027;#&#x0027;<br \/>\n\t  and n <= len(Zeichenkette)+1<\/code><\/p>\n<p>Und so das Ergebnis:<\/p>\n<table border=\"1\">\n<tr>\n<td><strong>LfdNr<\/strong><\/td>\n<td><strong>TeilNr<\/strong><\/td>\n<td><strong>Teilst&#252;ck<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>1<\/td>\n<td>13242<\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>2<\/td>\n<td>12<\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>3<\/td>\n<td>blabli<\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>4<\/td>\n<td>233242<\/td>\n<\/tr>\n<tr>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<\/tr>\n<\/table>\n<p>Das ist aus Datenbanksicht, aber nicht optimal. W&#252;nschenswert w&#228;re ein pivotiertes Ergebnis:<\/p>\n<table border=\"1\">\n<tr>\n<td><strong>LfdNr<\/strong><\/td>\n<td><strong>attr1<\/strong><\/td>\n<td><strong>attr2<\/strong><\/td>\n<td><strong>attr3<\/strong><\/td>\n<td><strong>attr4<\/strong><\/td>\n<td><strong>attr5<\/strong><\/td>\n<td><strong>attr6<\/strong><\/td>\n<td><strong>attr7<\/strong><\/td>\n<td><strong>attr8<\/strong><\/td>\n<td><strong>attr9<\/strong><\/td>\n<td><strong>attr10<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>13242<\/td>\n<td>22<\/td>\n<td>blabla<\/td>\n<td>233242<\/td>\n<td>blubblub<\/td>\n<td>12321<\/td>\n<td>1312<\/td>\n<td>1131<\/td>\n<td><\/td>\n<td>13123<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>13332<\/td>\n<td>12<\/td>\n<td>blibla<\/td>\n<td>233242<\/td>\n<td>blubblub<\/td>\n<td>12321<\/td>\n<td>1312<\/td>\n<td>1131<\/td>\n<td><\/td>\n<td>13123<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>13353<\/td>\n<td>21<\/td>\n<td>blabli<\/td>\n<td>233242<\/td>\n<td>blubblub<\/td>\n<td>12321<\/td>\n<td>1312<\/td>\n<td>1131<\/td>\n<td><\/td>\n<td>13123<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>13242<\/td>\n<td>12<\/td>\n<td>blibli<\/td>\n<td>233242<\/td>\n<td>blubblub<\/td>\n<td>12321<\/td>\n<td>1312<\/td>\n<td>1131<\/td>\n<td><\/td>\n<td>13123<\/td>\n<\/tr>\n<tr>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<td>&#8230;<\/td>\n<\/tr>\n<\/table>\n<p>Das kann man dann ganz einfach mit dem PIVOT-Konstrukt erreichen:<\/p>\n<p><code lang=\"sql\">SELECT *<br \/>\nFROM (select<br \/>\n\t\t\tLfdNr,<br \/>\n\t\t\t&#x0027;attr&#x0027;+convert(varchar,n-len(replace(substring(&#x0027;#'+Zeichenkette, 1, n), &#x0027;#&#x0027;, &#x0027;&#x0027;))) as &quot;Attribute&quot;,<br \/>\n\t\t\tsubstring(&#x0027;#'+Zeichenkette, n+1, charindex(&#x0027;#',Zeichenkette+&#x0027;#',n)-n) as &quot;Teilst&#252;ck&quot;<br \/>\n\t\tfrom LegacyInput<br \/>\n\t\tcross join hilfstabelle<br \/>\n\t\twhere substring(&#x0027;#'+Zeichenkette, n, 1)=&#x0027;#&#x0027;<br \/>\n\t\t  and n <= len(Zeichenkette)+1\n\t  ) AS ATR\n  PIVOT\n  (\n    MAX(\"Teilst&#252;ck\")\n    FOR attribute IN([attr1], [attr2], [attr3], [attr4], [attr5], [attr6], [attr7], [attr8], [attr9], [attr10])\n  ) AS PVT<\/code><\/p>\n<p>Das komplette Beispiel steht auch zum <a href=\"\/files\/samples\/stringsaufteilen.sql\">Download<\/a> bereit. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gestern kam mein Kollege Alexander mit einer ganz spannenden SQL-Fragestellung zu mir. weil die Anwendung auch noch den SQL-Server-2000 unterst&#252;tzt, mussten wir etwas in die Trickkiste greifen. Ich habe die Fragestellung jetzt daheim noch mal unter dem SQL-Server-2005 nachvollzogen und konnte dabei ein paar der schicken, neuen Features nutzen&#8230; Die Frage ist, wie man Strings, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[],"_links":{"self":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/436"}],"collection":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/comments?post=436"}],"version-history":[{"count":0,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/436\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/media?parent=436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/categories?post=436"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/tags?post=436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}