{"id":1050,"date":"2008-05-03T18:51:13","date_gmt":"2008-05-03T16:51:13","guid":{"rendered":"http:\/\/www.glorf.it\/blog\/2008\/05\/03\/sql-talk\/performance\/sql-server-2005-schnelles-pivot"},"modified":"2008-12-02T23:45:10","modified_gmt":"2008-12-02T22:45:10","slug":"sql-server-2005-schnelles-pivot","status":"publish","type":"post","link":"http:\/\/www.glorf.it\/blog\/2008\/05\/03\/sql-talk\/performance\/sql-server-2005-schnelles-pivot","title":{"rendered":"SQL-Server-2005: schnelles Pivot?"},"content":{"rendered":"<p>Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Der Quellcode zu diesem Artikel steht &#252;brigens <a href=\"http:\/\/www.glorf.it\/files\/200710_pivot\/01_selfmade_pivot.sql\">hier<\/a>.<\/p>\n<ul>\n<li><a href=\"http:\/\/www.glorf.it\/blog\/2008\/05\/03\/sql-talk\/performance\/sql-server-2005-schnelles-pivot\">Artikel 1: Wie wird Pivot abgearbeitet?<\/a><\/li>\n<li><a href=\"http:\/\/www.glorf.it\/blog\/2008\/05\/05\/sql-talk\/performance\/sql-server-2005-stoerer-beim-pivot-eliminieren\">Artikel 2: St&#246;rer beim PIVOT eliminieren<\/a><\/li>\n<li><a href=\"http:\/\/www.glorf.it\/blog\/2008\/05\/07\/sql-talk\/performance\/sql-server-2005-pivot-und-performance\">Artikel 3: Pivot-Performance<\/a><\/li>\n<li><a href=\"http:\/\/www.glorf.it\/blog\/2008\/05\/09\/sql-talk\/performance\/sql-server-2005-pivot-auf-joins\">Artikel 4: Pivot-Performance mit Joins<\/a><\/li>\n<\/ul>\n<p><strong>Wie wird PIVOT ausgef&#252;hrt?<\/strong><\/p>\n<p>Angenommen die Tabelle \u201eOpenSchema\u201c besteht aus drei Spalten: einer ID, einem Attributbezeichner und einer Wert-Spalte. Das s&#228;he etwa so aus:<br \/>\n<img src='http:\/\/www.glorf.it\/blog\/wp-content\/uploads\/2008\/05\/tabopenschema_2.jpg' alt='Tabelle Openschema' \/><\/p>\n<div class=\"small\">Diese Tabelle ist &#252;brigens einem Beispiel von Itzik Ben-Gan (siehe <a href=\"http:\/\/www.sql.co.il\">www.sql.co.il<\/a>) entnommen, weil er das PIVOT-Statement am besten erkl&#228;ren kann. Ehre wem Ehre geb&#252;hrt.<\/div>\n<p>Um die Ergebnisse dieser &quot;Kennziffer-Wert-Tabelle&quot; in &quot;relationaler&quot; Form zu pr&#228;sentieren, m&#252;ssen die Ergebnisse pivotiert werden:<br \/>\n<img src='http:\/\/www.glorf.it\/blog\/wp-content\/uploads\/2008\/05\/gewuenschtesergebnis_2.jpg' alt='gew&#252;nschtes Ergebnis'  \/><\/p>\n<p>Vor dem SQL-Server-2005 war dazu ein recht kompliziert aussehendes Konstrukt notwendig: Man musste pro Ergebnis eine Aggregatfunktion auswerten, dass intern mittels CASE nur die gew&#252;nschten Ergebnisse auswertet:<\/p>\n<p><code lang=\"sql\">SELECT ID,<br \/>\n\tMAX(CASE WHEN &quot;Attribute&quot; = &#x0027;attr1&#x0027; THEN &quot;VALUE&quot; END) as &quot;Typ&quot;,<br \/>\n\tMAX(CASE WHEN &quot;Attribute&quot; = &#x0027;attr2&#x0027; THEN &quot;VALUE&quot; END) as &quot;Datum&quot;,<br \/>\n\tMAX(CASE WHEN &quot;Attribute&quot; = &#x0027;attr3&#x0027; THEN &quot;VALUE&quot; END) as &quot;Anzahl&quot;,<br \/>\n\tMAX(CASE WHEN &quot;Attribute&quot; = &#x0027;attr4&#x0027; THEN &quot;VALUE&quot; END) as &quot;Dings&quot;,<br \/>\n\tMAX(CASE WHEN &quot;Attribute&quot; = &#x0027;attr5&#x0027; THEN &quot;VALUE&quot; END) as &quot;Bums&quot;<br \/>\nFROM OpenSchema<br \/>\nGROUP by ID<br \/>\nORDER BY ID<\/code><\/p>\n<p>In dieser Form stellten es Rozenshtein, Abranovich und Birger schon 1992 im &quot;SQL Server Forum 12\/1992&quot;  vor. Damals gab es nur das CASE-Konstrukt noch nicht, deswegen verwendeten sie verschiedene Tricks, um ein CASE auf mathematische Weise zu erreichen. Der Vorteil bei dieser alten Schreibweise ist, dass man die Gruppierung explizit angeben kann.<\/p>\n<p><strong>Pivotieren mit Pivot<\/strong><\/p>\n<p>Im SQL-Server-2005 f&#252;hrte Microsoft bekanntlich das PIVOT-Konstrukt ein, um das Ganze zu vereinfachen. Im PIVOT-Statement wird die Gruppierung hingegen implizit durchgef&#252;hrt: Es wird nach allen Spalten der Tabelle gruppiert, die nicht in den Klammern des PIVOT genannt werden. Unten werden &quot;Value&quot; und &quot;Attribute&quot; genannt, also wird nach allen verbleibenden gruppiert: hier nur &quot;ID&quot;.<\/p>\n<p><code lang=\"sql\">SELECT ID,<br \/>\n\tattr1 as Typ,<br \/>\n\tattr2 as Datum,<br \/>\n\tattr3 as Anzahl,<br \/>\n\tattr4 as Dings,<br \/>\n\tattr5 as Bums<br \/>\nFROM OpenSchema<br \/>\n\tPIVOT\t(<br \/>\n\t    MAX(&quot;Value&quot;)<br \/>\n\t    FOR Attribute IN (&quot;attr1&quot;, &quot;attr2&quot;, &quot;attr3&quot;, &quot;attr4&quot;, &quot;attr5&quot;)<br \/>\n\t) AS pvt<br \/>\nORDER BY ID<\/code><\/p>\n<p>Das PIVOT-Konstrukt ist genauso implementiert, wie es bereits oben beschrieben wurde. Das schlie&#223;e ich daraus, dass in beiden F&#228;llen der gleiche Zugriffsplan verwendet wird:<br \/>\n<img src='http:\/\/www.glorf.it\/blog\/wp-content\/uploads\/2008\/05\/pivot_executionplan.jpg' alt='Zugriffsplan' \/><\/p>\n<p>Nat&#252;rlich passiert es &#246;fters, dass der Optimizer einen semantisch &#228;quivalenten Zugriffsplan verwendet, wenn er effizienter ist. Das ist hier jedoch unwahrscheinlich.<br \/>\nDie &quot;&#228;ltere&quot; Form hilft mir zu verstehen, wann ein PIVOT schnell ausgef&#252;hrt werden kann und wann er einfach langsam sein <em>muss<\/em>. Sie hat zudem den Vorteil, dass man pro Spalte entscheiden kann, welche Aggregatfunktion verwendet werden soll. Beim neuen PIVOT wird f&#252;r alle die gleiche genutzt.<\/p>\n<div class=\"small\">Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. M&#246;glicherweise erscheint der Artikel noch dort. Ich ver&#246;ffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 veraltet&#8230; \ud83d\ude09<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Der Quellcode zu diesem Artikel steht &#252;brigens hier. Artikel 1: Wie wird Pivot [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[18],"tags":[930,51,929],"_links":{"self":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/1050"}],"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=1050"}],"version-history":[{"count":1,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/1050\/revisions"}],"predecessor-version":[{"id":1801,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/1050\/revisions\/1801"}],"wp:attachment":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/media?parent=1050"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/categories?post=1050"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/tags?post=1050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}