Tabelle:
UserId, Value, Date.
Ich möchte die UserId, den Wert für das Maximum (Datum) für jede UserId erhalten. Das heißt, der Wert für jede Benutzer-ID mit dem neuesten Datum. Gibt es eine Möglichkeit, dies einfach in SQL zu tun? (Vorzugsweise Oracle)
Update: Entschuldigung für etwaige Unklarheiten: Ich muss ALLE UserIds erhalten. Für jede Benutzer-ID jedoch nur die Zeile, in der dieser Benutzer das neueste Datum hat.
sql
oracle
greatest-n-per-group
Umang
quelle
quelle
Antworten:
Dadurch werden alle Zeilen abgerufen, für die der Spaltenwert my_date dem Maximalwert von my_date für diese Benutzer-ID entspricht. Dadurch werden möglicherweise mehrere Zeilen für die Benutzer-ID abgerufen, wobei das maximale Datum in mehreren Zeilen liegt.
"Analytische Funktionen rocken"
Edit: In Bezug auf den ersten Kommentar ...
"Die Verwendung von analytischen Abfragen und einer Selbstverknüpfung macht den Zweck von analytischen Abfragen zunichte."
In diesem Code gibt es keine Selbstverknüpfung. Stattdessen wird ein Prädikat auf das Ergebnis der Inline-Ansicht gesetzt, das die Analysefunktion enthält - eine ganz andere Angelegenheit und eine völlig übliche Praxis.
"Das Standardfenster in Oracle reicht von der ersten bis zur aktuellen Zeile in der Partition."
Die Fensterklausel gilt nur bei Vorliegen der Order-by-Klausel. Ohne order by-Klausel wird standardmäßig keine windowing-Klausel angewendet, und es kann keine explizit angegeben werden.
Der Code funktioniert.
quelle
MAX(...) OVER (...)
Sie auchROW_NUMBER() OVER (...)
(für die Top-n-pro-Gruppe) oderRANK() OVER (...)
(für die größte-n-pro-Gruppe) verwenden.Ich sehe, dass viele Leute Unterabfragen oder herstellerspezifische Funktionen verwenden, um dies zu tun, aber ich mache diese Art von Abfrage oft ohne Unterabfragen auf folgende Weise. Es verwendet einfaches Standard-SQL, sodass es in jeder RDBMS-Marke funktionieren sollte.
Mit anderen Worten: Rufen Sie die Zeile ab, von der
t1
keine andere Zeile mit demselbenUserId
und einem größeren Datum vorhanden ist.(Ich habe den Bezeichner "Datum" in Trennzeichen gesetzt, da es sich um ein reserviertes SQL-Wort handelt.)
Falls
t1."Date" = t2."Date"
, erscheint eine Verdoppelung. Normalerweise haben Tabellenauto_inc(seq)
Schlüssel, zid
. Um eine Verdoppelung zu vermeiden, kann Folgendes verwendet werden:Kommentar von @Farhan:
Hier ist eine detailliertere Erklärung:
Eine äußere Verknüpfung versucht, eine Verknüpfung
t1
herzustellent2
. Standardmäßig werden alle Ergebnisse vont1
zurückgegeben, und wenn eine Übereinstimmung vorliegtt2
, wird diese ebenfalls zurückgegeben. Wennt2
für eine bestimmte Zeile von keine Übereinstimmung vorliegtt1
, gibt die Abfrage weiterhin die Zeile von zurückt1
und wirdNULL
als Platzhalter für allet2
Spalten von verwendet. So funktionieren äußere Verknüpfungen im Allgemeinen.Der Trick bei dieser Abfrage besteht darin, die Übereinstimmungsbedingung des Joins so zu gestalten, dass sie
t2
mit derselben übereinstimmtuserid
und eine größeredate
. Die Idee ist , wenn eine Zeile in besteht ,t2
dass eine größeredate
, dann die Zeile int1
es verglichen kann nicht die größte sein ,date
dafüruserid
. Aber wenn es keine Übereinstimmung gibt - dh wenn keine Zeilet2
mit einer größerendate
als der Zeile in vorhanden istt1
- wissen wir, dass die Zeile int1
die Zeile mit der größtendate
für die gegebene waruserid
.In diesen Fällen (wenn gibt es keine Übereinstimmung), der die Spalten
t2
werdenNULL
- auch die in der angegebenen Spalten Joinbedingung. Deshalb verwenden wirWHERE t2.UserId IS NULL
, weil wir nach Fällen suchen, in denen keine Zeile mit einer größerendate
für die angegebene gefunden wurdeuserid
.quelle
quelle
Ich kenne Ihre genauen Spaltennamen nicht, aber es wäre ungefähr so:
quelle
Da ich nicht bei der Arbeit bin, habe ich Oracle nicht zur Hand, aber ich erinnere mich, dass Oracle das Abgleichen mehrerer Spalten in einer IN-Klausel zulässt, wodurch zumindest die Optionen vermieden werden sollten, die eine korrelierte Unterabfrage verwenden, was selten gut ist Idee.
So etwas vielleicht (kann mich nicht erinnern, ob die Spaltenliste in Klammern stehen sollte oder nicht):
EDIT: Habe es gerade wirklich ausprobiert:
So funktioniert es, obwohl einige der an anderer Stelle erwähnten New-Fangly-Sachen möglicherweise performanter sind.
quelle
Ich weiß, dass Sie nach Oracle gefragt haben, aber in SQL 2005 verwenden wir jetzt Folgendes:
quelle
Ich habe kein Oracle, um es zu testen, aber die effizienteste Lösung ist die Verwendung von analytischen Abfragen. Es sollte ungefähr so aussehen:
Ich vermute, dass Sie die äußere Abfrage loswerden und die innere deutlich machen können, aber ich bin mir nicht sicher. In der Zwischenzeit weiß ich, dass dieser funktioniert.
Wenn Sie mehr über analytische Abfragen erfahren möchten, empfehlen wir Ihnen, http://www.orafaq.com/node/55 und
http://www.akadia.com/services/ora_analytic_functions.html zulesen. Hier ist die kurze Zusammenfassung.Unter der Haube analysieren analytische Abfragen den gesamten Datensatz und verarbeiten ihn dann nacheinander. Während Sie es verarbeiten, partitionieren Sie das Dataset nach bestimmten Kriterien und sehen dann für jede Zeile ein Fenster (standardmäßig der erste Wert in der Partition für die aktuelle Zeile - dieser Standard ist auch der effizienteste) und können Werte mit a berechnen Anzahl der Analysefunktionen (deren Liste den Aggregatfunktionen sehr ähnlich ist).
In diesem Fall ist hier, was die innere Abfrage tut. Der gesamte Datensatz wird nach Benutzer-ID und dann nach Datum DESC sortiert. Dann verarbeitet es es in einem Durchgang. Für jede Zeile geben Sie die Benutzer-ID und das erste Datum zurück, das für diese Benutzer-ID angezeigt wird (da die Daten nach DESC sortiert sind, ist dies das maximale Datum). Dies gibt Ihnen Ihre Antwort mit doppelten Zeilen. Dann quetscht das äußere DISTINCT Duplikate.
Dies ist kein besonders spektakuläres Beispiel für analytische Abfragen. Für einen viel größeren Gewinn sollten Sie eine Tabelle mit Finanzbelegen erstellen und für jeden Benutzer und jede Quittung eine laufende Summe der von ihnen bezahlten Beträge berechnen. Analytische Abfragen lösen das effizient. Andere Lösungen sind weniger effizient. Aus diesem Grund sind sie Teil des SQL-Standards von 2003. (Leider hat Postgres sie noch nicht. Grrr ...)
quelle
Wäre eine QUALIFY-Klausel nicht sowohl einfach als auch am besten?
Für Teradata wird hier ein anständiger Größentest in 17 Sekunden mit dieser QUALIFY-Version und in 23 Sekunden mit der Inline-Ansicht / Aldridge-Lösung Nr. 1 ausgeführt.
quelle
rank()
Funktion in Situationen, in denen es Bindungen gibt. Sie könnten mit mehr als einem endenrank=1
. Besser zu verwenden,row_number()
wenn Sie wirklich nur einen Datensatz zurückgeben möchten.QUALIFY
Klausel für Teradata spezifisch ist. In Oracle müssen Sie (zumindest) Ihre Abfrage verschachteln und mithilfe einerWHERE
Klausel in der Wrapping-Select-Anweisung filtern (was wahrscheinlich die Leistung beeinträchtigt, würde ich mir vorstellen).In
Oracle 12c+
können Sie Top n- Abfragen zusammen mit der Analysefunktion verwendenrank
, um dies ohne Unterabfragen sehr präzise zu erreichen :Das obige gibt alle Zeilen mit max my_date pro Benutzer zurück.
Wenn Sie nur eine Zeile mit maximalem Datum möchten, ersetzen Sie die
rank
durchrow_number
:quelle
Verwenden Sie
ROW_NUMBER()
diese Option,Date
um jedem absteigend eine eindeutige Rangfolge zuzuweisenUserId
, und filtern Sie dann für jede Zeile in die erste ZeileUserId
(dhROW_NUMBER
= 1).quelle
Mit PostgreSQL 8.4 oder höher können Sie Folgendes verwenden:
quelle
Ich denke, Sie sollten diese Variante zur vorherigen Abfrage machen:
quelle
quelle
Musste nur ein "Live" -Beispiel bei der Arbeit schreiben :)
Dieser unterstützt mehrere Werte für UserId am selben Datum.
Spalten: Benutzer-ID, Wert, Datum
Sie können FIRST_VALUE anstelle von MAX verwenden und im EXPLAIN-Plan nachschlagen. Ich hatte keine Zeit damit zu spielen.
Wenn Sie große Tabellen durchsuchen, ist es wahrscheinlich besser, wenn Sie in Ihrer Abfrage VOLLSTÄNDIGE Hinweise verwenden.
quelle
quelle
Ich denke so etwas. (Verzeihen Sie mir etwaige Syntaxfehler; ich bin es gewohnt, an dieser Stelle HQL zu verwenden!)
EDIT: Auch die Frage falsch verstanden! Die Abfrage wurde korrigiert ...
quelle
(T-SQL) Holen Sie sich zuerst alle Benutzer und deren maximales Datum. Verbinden Sie sich mit der Tabelle, um die entsprechenden Werte für die Benutzer an den maximalen Daten zu finden.
Ergebnisse:
quelle
Die Antwort hier ist nur Oracle. Hier ist eine etwas komplexere Antwort in allen SQL-Anweisungen:
Wer hat das beste Gesamtergebnis bei den Hausaufgaben (maximale Summe der Hausaufgabenpunkte)?
Und ein schwierigeres Beispiel, das einer Erklärung bedarf, für das ich keine Zeit habe atm:
Geben Sie das Buch (ISBN und Titel) an, das 2008 am beliebtesten ist, dh das 2008 am häufigsten ausgeliehen wurde.
Hoffe das hilft (jedem) .. :)
Grüße, Guus
quelle
Angenommen, das Datum ist für eine bestimmte Benutzer-ID eindeutig. Hier einige TSQL:
quelle
Ich bin ziemlich spät zur Party, aber der folgende Hack übertrifft sowohl korrelierte Unterabfragen als auch alle Analysefunktionen, hat jedoch eine Einschränkung: Werte müssen in Zeichenfolgen konvertiert werden. Es funktioniert also für Datumsangaben, Zahlen und andere Zeichenfolgen. Der Code sieht nicht gut aus, aber das Ausführungsprofil ist großartig.
Der Grund, warum dieser Code so gut funktioniert, ist, dass er die Tabelle nur einmal scannen muss. Es sind keine Indizes erforderlich, und vor allem muss die Tabelle nicht sortiert werden, wie dies bei den meisten Analysefunktionen der Fall ist. Indizes sind jedoch hilfreich, wenn Sie das Ergebnis nach einer einzelnen Benutzer-ID filtern müssen.
quelle
IMHO funktioniert das. HTH
quelle
Ich denke das sollte funktionieren?
quelle
Beim ersten Versuch habe ich die Frage falsch verstanden. Nach der Top-Antwort finden Sie hier ein vollständiges Beispiel mit korrekten Ergebnissen:
- -
- -
quelle
Dies kümmert sich auch um Duplikate (geben Sie eine Zeile für jede Benutzer-ID zurück):
quelle
Gerade getestet und es scheint auf einer Protokollierungstabelle zu funktionieren
quelle
Dies sollte so einfach sein wie:
quelle
Lösung für MySQL ohne Partitionskonzepte KEEP, DENSE_RANK.
Referenz: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
quelle
Wenn Sie Postgres verwenden, können Sie
array_agg
like verwendenIch bin mit Oracle nicht vertraut. Das habe ich mir ausgedacht
Beide Abfragen liefern die gleichen Ergebnisse wie die akzeptierte Antwort. Siehe SQLFiddles:
quelle
Wenn (Benutzer-ID, Datum) eindeutig ist, dh kein Datum zweimal für denselben Benutzer angezeigt wird, dann:
quelle
quelle