Laut MSDN ist Median in Agact-SQL nicht als Aggregatfunktion verfügbar. Ich möchte jedoch herausfinden, ob es möglich ist, diese Funktionalität zu erstellen (mithilfe der Funktion " Aggregat erstellen" , einer benutzerdefinierten Funktion oder einer anderen Methode).
Was wäre der beste Weg (wenn möglich), dies zu tun - die Berechnung eines Medianwerts (unter der Annahme eines numerischen Datentyps) in einer aggregierten Abfrage zu ermöglichen?
sql
sql-server
aggregate-functions
median
Yaakov Ellis
quelle
quelle
Antworten:
2019 UPDATE: In den 10 Jahren, seit ich diese Antwort geschrieben habe, wurden mehr Lösungen gefunden, die zu besseren Ergebnissen führen können. Außerdem haben SQL Server-Versionen seitdem (insbesondere SQL 2012) neue T-SQL-Funktionen eingeführt, mit denen Mediane berechnet werden können. SQL Server-Versionen haben auch das Abfrageoptimierungsprogramm verbessert, das sich auf die Leistung verschiedener Medianlösungen auswirken kann. Net-net, mein ursprünglicher Beitrag von 2009 ist noch in Ordnung, aber es gibt möglicherweise bessere Lösungen für moderne SQL Server-Apps. Schauen Sie sich diesen Artikel aus dem Jahr 2012 an, der eine großartige Ressource darstellt: https://sqlperformance.com/2012/08/t-sql-queries/median
In diesem Artikel wurde festgestellt, dass das folgende Muster viel, viel schneller ist als alle anderen Alternativen, zumindest in Bezug auf das einfache Schema, das sie getestet haben. Diese Lösung war 373x schneller (!!!) als die langsamste (
PERCENTILE_CONT
) getestete Lösung. Beachten Sie, dass für diesen Trick zwei separate Abfragen erforderlich sind, die möglicherweise nicht in allen Fällen praktikabel sind. Es erfordert auch SQL 2012 oder höher.Nur weil ein Test mit einem Schema im Jahr 2012 großartige Ergebnisse erbracht hat, kann Ihr Kilometerstand natürlich variieren, insbesondere wenn Sie mit SQL Server 2014 oder höher arbeiten. Wenn perf für Ihre Medianberechnung wichtig ist, würde ich dringend empfehlen, mehrere der in diesem Artikel empfohlenen Optionen auszuprobieren und zu testen, um sicherzustellen, dass Sie die beste für Ihr Schema gefunden haben.
Ich würde auch besonders vorsichtig sein, wenn ich die Funktion (neu in SQL Server 2012) verwende
PERCENTILE_CONT
, die in einer der anderen Antworten auf diese Frage empfohlen wird , da der oben verlinkte Artikel feststellte, dass diese integrierte Funktion 373-mal langsamer ist als die schnellste Lösung. Es ist möglich, dass sich diese Ungleichheit in den letzten 7 Jahren verbessert hat, aber ich persönlich würde diese Funktion nicht für einen großen Tisch verwenden, bis ich ihre Leistung im Vergleich zu anderen Lösungen überprüft habe.ORIGINAL 2009 POST IST UNTEN:
Es gibt viele Möglichkeiten, dies zu tun, mit dramatisch variierender Leistung. Hier ist eine besonders gut optimierte Lösung aus Median, ROW_NUMBERs und Leistung . Dies ist eine besonders optimale Lösung, wenn es um tatsächliche E / A geht, die während der Ausführung generiert werden. Sie sieht teurer aus als andere Lösungen, ist aber tatsächlich viel schneller.
Diese Seite enthält auch eine Diskussion anderer Lösungen und Details zu Leistungstests. Beachten Sie die Verwendung einer eindeutigen Spalte als Disambiguator, falls mehrere Zeilen mit demselben Wert der Medianspalte vorhanden sind.
Versuchen Sie wie bei allen Datenbankleistungsszenarien immer, eine Lösung mit realen Daten auf realer Hardware zu testen. Sie wissen nie, wann eine Änderung des SQL Server-Optimierers oder eine Besonderheit in Ihrer Umgebung eine normalerweise schnelle Lösung verlangsamt.
quelle
Wenn Sie SQL 2005 oder besser verwenden, ist dies eine nette, einfache Medianberechnung für eine einzelne Spalte in einer Tabelle:
quelle
select gid, median(score) from T group by gid
. Benötigen Sie dafür eine korrelierte Unterabfrage?In SQL Server 2012 sollten Sie PERCENTILE_CONT verwenden :
Siehe auch: http://blog.sqlauthority.com/2011/11/20/sql-server-introduction-to-percentile_cont-analytic-functions-introduced-in-sql-server-2012/
quelle
DISTINCT
oder hinzufügenGROUPY BY SalesOrderID
? Andernfalls haben Sie viele doppelte Zeilen.PERCENTILE_DISC
Meine ursprüngliche schnelle Antwort war:
Dies gibt Ihnen den Median und den Interquartilbereich auf einen Schlag. Wenn Sie wirklich nur eine Zeile als Median möchten, kommentieren Sie die where-Klausel aus.
Wenn Sie dies in einen Erklärungsplan einfügen, sortieren 60% der Arbeit die Daten, was bei der Berechnung derartiger positionsabhängiger Statistiken unvermeidbar ist.
Ich habe die Antwort geändert, um dem ausgezeichneten Vorschlag von Robert Ševčík-Robajz in den Kommentaren unten zu folgen:
Dies sollte die korrekten Median- und Perzentilwerte berechnen, wenn Sie eine gerade Anzahl von Datenelementen haben. Kommentieren Sie die letzte where-Klausel erneut aus, wenn Sie nur den Median und nicht die gesamte Perzentilverteilung möchten.
quelle
Noch besser:
Vom Meister selbst, Itzik Ben-Gan !
quelle
MS SQL Server 2012 (und höher) verfügt über die Funktion PERCENTILE_DISC, die ein bestimmtes Perzentil für sortierte Werte berechnet. PERCENTILE_DISC (0.5) berechnet den Median - https://msdn.microsoft.com/en-us/library/hh231327.aspx
quelle
Einfach, schnell, genau
quelle
Wenn Sie die Funktion "Aggregat erstellen" in SQL Server verwenden möchten, gehen Sie wie folgt vor. Auf diese Weise haben Sie den Vorteil, dass Sie saubere Abfragen schreiben können. Beachten Sie, dass dieser Prozess angepasst werden kann, um einen Perzentilwert ziemlich einfach zu berechnen.
Erstellen Sie ein neues Visual Studio-Projekt und setzen Sie das Zielframework auf .NET 3.5 (dies ist für SQL 2008, in SQL 2012 kann es anders sein). Erstellen Sie dann eine Klassendatei und geben Sie den folgenden Code oder ein c # -Äquivalent ein:
Kompilieren Sie es dann, kopieren Sie die DLL- und PDB-Datei auf Ihren SQL Server-Computer und führen Sie den folgenden Befehl in SQL Server aus:
Sie können dann eine Abfrage schreiben, um den Median wie folgt zu berechnen: SELECT dbo.Median (Field) FROM Table
quelle
Ich bin gerade auf diese Seite gestoßen, als ich nach einer satzbasierten Lösung für den Median gesucht habe. Nachdem ich mir einige der Lösungen hier angesehen hatte, kam ich auf Folgendes. Hoffnung ist Hilfe / funktioniert.
quelle
Die folgende Abfrage gibt den Median aus einer Liste von Werten in einer Spalte zurück. Es kann nicht als oder zusammen mit einer Aggregatfunktion verwendet werden, Sie können es jedoch weiterhin als Unterabfrage mit einer WHERE-Klausel in der inneren Auswahl verwenden.
SQL Server 2005+:
quelle
Obwohl die Lösung von Justin Grant solide erscheint, stellte ich fest, dass bei einer Anzahl von doppelten Werten innerhalb eines bestimmten Partitionsschlüssels die Zeilennummern für die doppelten ASC-Werte nicht in der richtigen Reihenfolge sind, sodass sie nicht richtig ausgerichtet sind.
Hier ist ein Fragment aus meinem Ergebnis:
Ich habe Justins Code als Grundlage für diese Lösung verwendet. Obwohl es angesichts der Verwendung mehrerer abgeleiteter Tabellen nicht so effizient ist, löst es das aufgetretene Problem der Zeilenreihenfolge. Verbesserungen wären willkommen, da ich nicht so viel Erfahrung mit T-SQL habe.
quelle
Justins Beispiel oben ist sehr gut. Dieser Primärschlüsselbedarf sollte jedoch sehr deutlich angegeben werden. Ich habe diesen Code in freier Wildbahn ohne den Schlüssel gesehen und die Ergebnisse sind schlecht.
Die Beschwerde, die ich über Percentile_Cont bekomme, ist, dass es Ihnen keinen tatsächlichen Wert aus dem Datensatz gibt. Verwenden Sie Percentile_Disc, um zu einem "Median" zu gelangen, der ein tatsächlicher Wert aus dem Datensatz ist.
quelle
Schreiben Sie in einer UDF:
quelle
Median Finding
Dies ist die einfachste Methode, um den Median eines Attributs zu ermitteln.
quelle
Weitere Lösungen für die Medianberechnung in SQL finden Sie hier: " Einfache Methode zur Berechnung des Medians mit MySQL " (die Lösungen sind größtenteils herstellerunabhängig).
quelle
Für eine stetige Variable / Maßnahme 'col1' aus 'table1'
quelle
Mit dem COUNT-Aggregat können Sie zunächst zählen, wie viele Zeilen vorhanden sind, und in einer Variablen namens @cnt speichern. Anschließend können Sie Parameter für den OFFSET-FETCH-Filter berechnen, um basierend auf der Mengenreihenfolge anzugeben, wie viele Zeilen übersprungen werden sollen (Versatzwert) und wie viele gefiltert werden sollen (Abrufwert).
Die Anzahl der zu überspringenden Zeilen beträgt (@cnt - 1) / 2. Es ist klar, dass diese Berechnung für eine ungerade Anzahl korrekt ist, da Sie zuerst 1 für den einzelnen Mittelwert subtrahieren, bevor Sie durch 2 dividieren.
Dies funktioniert auch bei einer geraden Zählung korrekt, da die im Ausdruck verwendete Division eine ganzzahlige Division ist. Wenn Sie also 1 von einer geraden Zählung abziehen, bleibt ein ungerader Wert übrig.
Wenn Sie diesen ungeraden Wert durch 2 teilen, wird der Bruchteil des Ergebnisses (.5) abgeschnitten. Die Anzahl der abzurufenden Zeilen beträgt 2 - (@cnt% 2). Die Idee ist, dass wenn die Anzahl ungerade ist, das Ergebnis der Modulo-Operation 1 ist und Sie 1 Zeile abrufen müssen. Wenn die Anzahl gerade ist, ist das Ergebnis der Modulo-Operation 0, und Sie müssen 2 Zeilen abrufen. Durch Subtrahieren des 1- oder 0-Ergebnisses der Modulo-Operation von 2 erhalten Sie die gewünschte 1 bzw. 2. Um schließlich die Mediangröße zu berechnen, nehmen Sie die eine oder zwei Ergebnisgrößen und wenden Sie einen Durchschnitt an, nachdem Sie den eingegebenen ganzzahligen Wert wie folgt in einen numerischen Wert konvertiert haben:
quelle
Ich wollte selbst eine Lösung finden, aber mein Gehirn stolperte und fiel auf den Weg. Ich denke, es funktioniert, aber bitte mich nicht, es am Morgen zu erklären. : P.
quelle
quelle
Dies funktioniert mit SQL 2000:
quelle
Für Neulinge wie mich, die die Grundlagen lernen, finde ich es persönlich einfacher, diesem Beispiel zu folgen, da es einfacher ist, genau zu verstehen, was passiert und woher die Medianwerte kommen ...
In absoluter Ehrfurcht vor einigen der oben genannten Codes !!!
quelle
Dies ist eine so einfache Antwort, wie ich sie mir vorstellen kann. Hat gut mit meinen Daten funktioniert. Wenn Sie bestimmte Werte ausschließen möchten, fügen Sie der inneren Auswahl einfach eine where-Klausel hinzu.
quelle
Die folgende Lösung funktioniert unter diesen Annahmen:
Code:
quelle
quelle
Ich versuche es mit mehreren Alternativen, aber da meine Datensätze wiederholte Werte haben, scheinen die ROW_NUMBER-Versionen für mich keine Wahl zu sein. Also hier die Abfrage, die ich verwendet habe (eine Version mit NTILE):
quelle
Aufbauend auf Jeff Atwoods Antwort oben wird mit GROUP BY und einer korrelierten Unterabfrage der Median für jede Gruppe ermittelt.
quelle
Häufig müssen wir den Median möglicherweise nicht nur für die gesamte Tabelle berechnen, sondern auch für Aggregate in Bezug auf eine ID. Mit anderen Worten, berechnen Sie den Median für jede ID in unserer Tabelle, wobei jede ID viele Datensätze enthält. (basierend auf der von @gdoron bearbeiteten Lösung: gute Leistung und funktioniert in vielen SQL)
Ich hoffe es hilft.
quelle
Für Ihre Frage hatte Jeff Atwood bereits die einfache und effektive Lösung angegeben. Wenn Sie jedoch nach einem alternativen Ansatz zur Berechnung des Medians suchen, hilft Ihnen der folgende SQL-Code.
Wenn Sie den Median in MySQL berechnen möchten , ist dieser Github-Link hilfreich.
quelle
Dies ist die optimalste Lösung, um Mediane zu finden, die mir einfallen. Die Namen im Beispiel basieren auf dem Beispiel von Justin. Stellen Sie sicher, dass ein Index für die Tabelle Sales.SalesOrderHeader mit den Indexspalten CustomerId und TotalDue in dieser Reihenfolge vorhanden ist.
AKTUALISIEREN
Ich war mir nicht sicher, welche Methode die beste Leistung aufweist. Daher habe ich einen Vergleich zwischen meiner Methode Justin Grants und Jeff Atwoods durchgeführt, indem ich eine Abfrage basierend auf allen drei Methoden in einem Stapel ausgeführt habe. Die Stapelkosten für jede Abfrage waren:
Ohne Index:
Und mit Index
Ich habe versucht zu sehen, wie gut die Abfragen skalieren, wenn Sie einen Index haben, indem Sie mehr Daten aus ungefähr 14 000 Zeilen um den Faktor 2 bis 512 erstellen, was am Ende ungefähr 7,2 Millionen Zeilen bedeutet. Hinweis Ich habe sichergestellt, dass das CustomeId-Feld bei jeder einzelnen Kopie eindeutig ist, sodass der Anteil der Zeilen im Vergleich zur eindeutigen Instanz von CustomerId konstant gehalten wurde. Während ich dies tat, führte ich Ausführungen aus, bei denen ich den Index anschließend neu erstellte, und bemerkte, dass sich die Ergebnisse mit den Daten, die ich zu diesen Werten hatte, auf einen Faktor von 128 stabilisierten:
Ich habe mich gefragt, wie sich die Skalierung der Anzahl der Zeilen auf die Leistung ausgewirkt haben könnte, aber die eindeutige Kunden-ID konstant gehalten hat. Deshalb habe ich einen neuen Test eingerichtet, bei dem ich genau dies getan habe. Anstatt sich zu stabilisieren, ging das Batch-Kostenverhältnis weiter auseinander, auch anstatt etwa 20 Zeilen pro Kunden-ID pro Durchschnitt, den ich am Ende hatte, ungefähr 10000 Zeilen pro solch eindeutiger ID. Die Zahlen wo:
Ich habe sichergestellt, dass ich jede Methode korrekt implementiert habe, indem ich die Ergebnisse verglichen habe. Mein Fazit ist, dass die von mir verwendete Methode im Allgemeinen schneller ist, solange ein Index vorhanden ist. Beachten Sie auch, dass diese Methode in diesem Artikel https://www.microsoftpressstore.com/articles/article.aspx?p=2314819&seqNum=5 für dieses spezielle Problem empfohlen wird
Eine Möglichkeit, die Leistung nachfolgender Aufrufe dieser Abfrage noch weiter zu verbessern, besteht darin, die Zählinformationen in einer Hilfstabelle beizubehalten. Sie können es sogar beibehalten, indem Sie einen Trigger haben, der Informationen zur Anzahl der SalesOrderHeader-Zeilen abhängig von der Kunden-ID aktualisiert und enthält. Natürlich können Sie dann auch einfach den Median speichern.
quelle
Für große Datensätze können Sie diese GIST ausprobieren:
https://gist.github.com/chrisknoll/1b38761ce8c5016ec5b2
Es aggregiert die unterschiedlichen Werte, die Sie in Ihrem Satz finden würden (z. B. Alter oder Geburtsjahr usw.), und verwendet SQL-Fensterfunktionen, um die in der Abfrage angegebene Perzentilposition zu ermitteln.
quelle