{"id":37,"date":"2006-07-17T18:16:09","date_gmt":"2006-07-17T16:16:09","guid":{"rendered":"http:\/\/www.glorf.it\/blog\/2006\/07\/17\/sql-talk\/median-am-sql-server-2005\/"},"modified":"2006-07-17T20:44:34","modified_gmt":"2006-07-17T18:44:34","slug":"median-am-sql-server-2005","status":"publish","type":"post","link":"http:\/\/www.glorf.it\/blog\/2006\/07\/17\/sql-talk\/median-am-sql-server-2005","title":{"rendered":"Median am SQL Server 2005"},"content":{"rendered":"<p>Ausgehend von dem <a href=\"http:\/\/www.glorf.it\/blog\/2006\/07\/08\/sql-talk\/headwriteline-das-median-problem-teil-2\/\">Posting letzte Woche<\/a> m&#246;chte eine L&#246;sung f&#252;r den Median am SQL Server 2005 vorstellen, die wegen der neuen TSQL-Features um einiges performanter ist, als die &quot;alte&quot; L&#246;sung: <\/p>\n<p>Zum Vergleich hier noch mal die &quot;beste&quot; L&#246;sung am SQL Server 2000:<\/p>\n<p><code lang=\"sql\">select cast(<br \/>\n          (select max(Quantity)<br \/>\n                from (    SELECT TOP 50 percent Quantity<br \/>\n                                   FROM #bla<br \/>\n                            order by Quantity asc) as d)<br \/>\n        +(select min(Quantity)<br \/>\n                from (    SELECT TOP 50 percent Quantity<br \/>\n                                   FROM #bla<br \/>\n                            order by Quantity desc) as d)<br \/>\n        as numeric(12,2))\/2 as &quot;richtiger Median bei ganzen Zahlen&quot;<\/code><\/p>\n<p>Diese L&#246;sung am SQL Server 2005 ist etwa um Faktor 4 schneller. Sie basiert auf dem Ansatz von Itzik Ben-Gan, den ich jetzt entdeckt habe: <a href=\"http:\/\/www.sqlmag.com\/Articles\/Index.cfm?ArticleID=49827&#038;DisplayTab=Article\" target=\"_blank\">&quot;Calculating the Median Gets Simpler in SQL Server 2005&quot; by Itzik Ben-Gan<\/a>. (Ein wenig bin ich stolz, dass er f&#252;r den 2000er die gleiche L&#246;sung vorschl&#228;gt wie ich.. \ud83d\ude09  )<\/p>\n<p>In einer Common-Table-Expression wird die komplette Liste durchnumeriert und gez&#228;hlt. Auf dem Ergebnis werden dann alle bis auf der bzw. die beiden mittleren Datens&#228;tze rausgefiltert (WHERE). Dann muss nur noch der Durchschnitt auf dem Rest berechnet werden:<\/p>\n<p><code lang=\"sql\">WITH blaRN AS<br \/>\n(<br \/>\n  SELECT Quantity,<br \/>\n    ROW_NUMBER() OVER(ORDER BY Quantity) AS RowNum,<br \/>\n    (select COUNT(*) from #bla) AS Cnt<br \/>\n  FROM #bla<br \/>\n)<br \/>\nSELECT avg(Quantity) as Median<br \/>\nFROM blaRN<br \/>\nWHERE RowNum IN((Cnt + 1) \/ 2, (Cnt + 2) \/ 2)<\/code><\/p>\n<p>Die n&#228;chste L&#246;sung f&#252;r den SQL 2005 basiert auf der Losung von <a href=\"http:\/\/headwriteline.blogspot.com\/2006\/04\/das-median-problem-teil-2.html\" target=\"_blank\">J&#246;rg Neumann<\/a>, liefert aber in allen F&#228;llen ein korrektes Ergebnis.<\/p>\n<p>Hier passiert im Prinzip das gleiche wie oben. Allerdings wird hier in einer Derived-Table durchgez&#228;hlt. In der &#228;u&#223;eren Query werden dann alle bis auf den bzw. die beiden mittleren Datens&#228;tze gefiltert. Dann muss nur noch deren Durchschnittswert berechnet werden:<\/p>\n<p><code lang=\"sql\">SELECT avg(Quantity) as Median<br \/>\nFROM<br \/>\n(<br \/>\n    SELECT ROW_NUMBER() OVER<br \/>\n    (<br \/>\n        ORDER BY Quantity DESC<br \/>\n    ) AS Rank,<br \/>\n    Quantity<br \/>\n    FROM #bla<br \/>\n) AS sub<br \/>\nWHERE (SELECT COUNT(*) FROM #bla) \/ 2 in (rank, rank-1)<\/code><\/p>\n<p>Der Zugriffplan der unteren L&#246;sung ist leicht komplizierter, aber das macht bei meinen paar Datens&#228;tzen keinen Unterschied aus&#8230; <\/p>\n<p>Einige sehr interessante Ans&#228;tze stehen &#252;brigens auch bei <a href=\"http:\/\/pluralsight.com\/blogs\/dan\/archive\/2005\/09\/28\/15073.aspx\" target=\"_blank\">Dan Sullivan<\/a>.<\/p>\n<p>Anbei noch der Code, um die Beispieltabelle zu f&#252;llen:<\/p>\n<p><code lang=\"sql\">if object_id(N'tempdb..#bla') is not null drop table #bla<br \/>\ncreate table #bla (quantity numeric(12,2))<br \/>\ncreate clustered index i1 on #bla(quantity)<\/p>\n<p>insert into #bla(quantity) values (1)<br \/>\ninsert into #bla(quantity) values (2)<br \/>\ninsert into #bla(quantity) values (3)<br \/>\ninsert into #bla(quantity) values (4)<br \/>\ninsert into #bla(quantity) values (5)<br \/>\ninsert into #bla(quantity) values (105)<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ausgehend von dem Posting letzte Woche m&#246;chte eine L&#246;sung f&#252;r den Median am SQL Server 2005 vorstellen, die wegen der neuen TSQL-Features um einiges performanter ist, als die &quot;alte&quot; L&#246;sung: Zum Vergleich hier noch mal die &quot;beste&quot; L&#246;sung am SQL Server 2000: select cast( (select max(Quantity) from ( SELECT TOP 50 percent Quantity FROM #bla [&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\/37"}],"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=37"}],"version-history":[{"count":0,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/37\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/media?parent=37"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/categories?post=37"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/tags?post=37"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}