Ich habe eine Tabelle, an der ich für jede Gruppe den neuesten Eintrag erhalten möchte. Hier ist die Tabelle:
DocumentStatusLogs
Tabelle
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
Die Tabelle wird DocumentID
nach DateCreated
absteigender Reihenfolge gruppiert und sortiert . Für jeden DocumentID
möchte ich den neuesten Status erhalten.
Meine bevorzugte Ausgabe:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
Gibt es eine Aggregatfunktion, um von jeder Gruppe nur die Spitze zu erhalten? Siehe Pseudocode
GetOnlyTheTop
unten:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Wenn eine solche Funktion nicht vorhanden ist, kann ich auf irgendeine Weise die gewünschte Ausgabe erzielen?
- Oder könnte dies in erster Linie durch eine nicht normalisierte Datenbank verursacht werden? Ich denke, da ich nur eine Zeile suche, sollte sich diese
status
auch in der übergeordneten Tabelle befinden?
Weitere Informationen finden Sie in der übergeordneten Tabelle:
Aktuelle Documents
Tabelle
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
Sollte die übergeordnete Tabelle so sein, damit ich leicht auf ihren Status zugreifen kann?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
UPDATE Ich habe gerade gelernt, wie man "anwenden" verwendet, was es einfacher macht, solche Probleme anzugehen.
Antworten:
Wenn Sie 2 Einträge pro Tag erwarten, wird dies willkürlich einen auswählen. Verwenden Sie stattdessen DENSE_RANK, um beide Einträge für einen Tag abzurufen
Ob normalisiert oder nicht, es hängt davon ab, ob Sie:
So wie es aussieht, behalten Sie den Statusverlauf bei. Wenn Sie auch den neuesten Status in der übergeordneten Tabelle wünschen (was Denormalisierung ist), benötigen Sie einen Auslöser, um den "Status" im übergeordneten Element beizubehalten. oder löschen Sie diese Statusverlaufstabelle.
quelle
Partition By
?With
ist auch neu für mich :( Ich benutze sowieso mssql 2005.ROW_NUMBER
eine Art einer Unterabfrage für jede Zeile?Ich habe gerade gelernt, wie man es benutzt
cross apply
. So verwenden Sie es in diesem Szenario:quelle
Ich habe einige Zeitangaben für die verschiedenen Empfehlungen hier gemacht, und die Ergebnisse hängen wirklich von der Größe der betreffenden Tabelle ab. Die konsistenteste Lösung ist jedoch die Verwendung der CROSS APPLY. Diese Tests wurden mit einer Tabelle mit SQL Server 2008-R2 ausgeführt 6.500 Datensätze und ein weiteres (identisches Schema) mit 137 Millionen Datensätzen. Die abgefragten Spalten sind Teil des Primärschlüssels in der Tabelle, und die Tabellenbreite ist sehr klein (ca. 30 Byte). Die Zeiten werden von SQL Server aus dem tatsächlichen Ausführungsplan gemeldet.
Ich denke, das wirklich Erstaunliche war, wie konstant die Zeit für die CROSS APPLY war, unabhängig von der Anzahl der beteiligten Zeilen.
quelle
Ich weiß, dass dies ein alter Thread ist, aber die
TOP 1 WITH TIES
Lösungen sind sehr nett und könnten hilfreich sein, um die Lösungen durchzulesen.Mehr zur TOP-Klausel finden Sie hier .
quelle
Wenn Sie sich Sorgen um die Leistung machen, können Sie dies auch mit MAX () tun:
ROW_NUMBER () erfordert eine Art aller Zeilen in Ihrer SELECT-Anweisung, MAX nicht. Sollte Ihre Anfrage drastisch beschleunigen.
quelle
row_number()
selbst bei ordnungsgemäßer Indizierung eine Outperformance erzielt. Ich finde es besonders wertvoll in Self-Join-Szenarien. Zu beachten ist jedoch, dass diese Methode trotz der Angabe niedriger Teilbaumkosten häufig eine höhere Anzahl von logischen Lesevorgängen und Scan-Zählungen liefert. Sie müssen die Kosten / Nutzen in Ihrem speziellen Fall abwägen, um festzustellen, ob es tatsächlich besser ist.Welcher Datenbankserver? Dieser Code funktioniert nicht bei allen.
In Bezug auf die zweite Hälfte Ihrer Frage erscheint es mir vernünftig, den Status als Spalte aufzunehmen. Du kannst gehen
DocumentStatusLogs
als Protokoll , aber dennoch die neuesten Informationen in der Haupttabelle speichern.Übrigens, wenn Sie die
DateCreated
Spalte bereits in der Tabelle "Dokumente" haben, können Sie sich einfach damit verbindenDocumentStatusLogs
(sofern dies inDateCreated
eindeutig istDocumentStatusLogs
).Bearbeiten: MsSQL unterstützt USING nicht. Ändern Sie es daher in:
quelle
max(DateCreated)
Dies ist eine der am leichtesten zu findenden Fragen zu diesem Thema, daher wollte ich eine moderne Antwort darauf geben (sowohl als Referenz als auch um anderen zu helfen). Mit
first_value
und könnenover
Sie die obige Abfrage kurz bearbeiten:Dies sollte in SQL Server 2008 und höher funktionieren.
First_value
kann als eine Möglichkeit angesehen werden, diesSelect Top 1
bei Verwendung einerover
Klausel zu erreichen.Over
Ermöglicht das Gruppieren in der Auswahlliste. Anstatt verschachtelte Unterabfragen zu schreiben (wie dies bei vielen vorhandenen Antworten der Fall ist), ist dies besser lesbar. Hoffe das hilft.quelle
Dies ist ein ziemlich alter Thread, aber ich dachte, ich würde meine zwei Cent genauso einwerfen, wie die akzeptierte Antwort für mich nicht besonders gut funktioniert hat. Ich habe die Lösung von gbn für ein großes Dataset ausprobiert und festgestellt, dass sie sehr langsam ist (> 45 Sekunden bei mehr als 5 Millionen Datensätzen in SQL Server 2012). Wenn man sich den Ausführungsplan ansieht, ist es offensichtlich, dass das Problem darin besteht, dass eine SORT-Operation erforderlich ist, die die Dinge erheblich verlangsamt.
Hier ist eine Alternative, die ich aus dem Entity-Framework entfernt habe, das keine SORT-Operation benötigt und eine NON-Clustered-Index-Suche durchführt. Dies reduziert die Ausführungszeit für den oben genannten Datensatz auf <2 Sekunden.
Jetzt gehe ich von etwas aus, das in der ursprünglichen Frage nicht vollständig angegeben ist. Wenn Ihr Tabellendesign jedoch so ist, dass Ihre ID-Spalte eine ID mit automatischer Inkrementierung ist und DateCreated bei jeder Einfügung auf das aktuelle Datum gesetzt wird, dann sogar Ohne meine obige Abfrage auszuführen, könnten Sie tatsächlich eine beträchtliche Leistungssteigerung für die Lösung von gbn erzielen (etwa die Hälfte der Ausführungszeit), wenn Sie nur nach ID bestellen, anstatt nach DateCreated zu bestellen, da dies eine identische Sortierreihenfolge liefert und eine schnellere Sortierung ist.
quelle
Mein Code zur Auswahl der Top 1 aus jeder Gruppe
quelle
Überprüfen von Clints fantastischer und korrekter Antwort von oben:
Die Leistung zwischen den beiden folgenden Abfragen ist interessant. 52% sind die Besten. Und 48% sind die zweiten. Eine 4% ige Leistungsverbesserung mit DISTINCT anstelle von ORDER BY. ORDER BY hat jedoch den Vorteil, nach mehreren Spalten zu sortieren.
Option 1:
Option 2:
Management Studio von M $: Nachdem Sie den ersten Block markiert und ausgeführt haben, markieren Sie Option 1 und Option 2. Klicken Sie mit der rechten Maustaste auf -> [Geschätzten Ausführungsplan anzeigen]. Führen Sie dann das gesamte Objekt aus, um die Ergebnisse anzuzeigen.
Option 1 Ergebnisse:
Option 2 Ergebnisse:
Hinweis:
Ich vermeide auch EXISTS / IN-Unterabfragen in der WHERE- oder ON-Klausel, da dies einige schreckliche Ausführungspläne verursacht hat. Der Kilometerstand variiert jedoch. Überprüfen Sie den Ausführungsplan und die Profilleistung, wo und wann immer dies erforderlich ist!
quelle
Diese Lösung kann verwendet werden, um die TOP N neuesten Zeilen für jede Partition abzurufen (im Beispiel ist N 1 in der WHERE-Anweisung und Partition doc_id):
quelle
Wenn Sie nur die letzte Dokumentbestellung von DateCreated zurückgeben möchten, wird nur das Top-1-Dokument von DocumentID zurückgegeben
quelle
CROSS APPLY
war die Methode, die ich für meine Lösung verwendet habe, da sie für mich und für die Bedürfnisse meiner Kunden funktioniert hat. Und nach dem, was ich gelesen habe, sollte die beste Gesamtleistung erzielt werden, sollte ihre Datenbank erheblich wachsen.quelle
Hier sind 3 verschiedene Ansätze für das vorliegende Problem zusammen mit den besten Indizierungsoptionen für jede dieser Abfragen (bitte probieren Sie die Indizes selbst aus und sehen Sie sich den logischen Lesevorgang, die verstrichene Zeit und den Ausführungsplan an. Ich habe die Vorschläge aus meiner Erfahrung mit geliefert solche Abfragen ohne Ausführung für dieses spezielle Problem).
Ansatz 1 : Verwenden von ROW_NUMBER (). Wenn der Rowstore-Index die Leistung nicht verbessern kann, können Sie den nicht gruppierten / gruppierten Columnstore-Index wie für Abfragen mit Aggregation und Gruppierung sowie für Tabellen ausprobieren, die ständig in verschiedenen Spalten sortiert sind. Der Columnstore-Index ist normalerweise die beste Wahl.
Ansatz 2 : Verwenden von FIRST_VALUE. Wenn der Rowstore-Index die Leistung nicht verbessern kann, können Sie den nicht gruppierten / gruppierten Columnstore-Index wie für Abfragen mit Aggregation und Gruppierung sowie für Tabellen ausprobieren, die ständig in verschiedenen Spalten sortiert sind. Der Columnstore-Index ist normalerweise die beste Wahl.
Ansatz 3 : CROSS APPLY verwenden. Das Erstellen eines Rowstore-Index für die DocumentStatusLogs-Tabelle, der die in der Abfrage verwendeten Spalten abdeckt, sollte ausreichen, um die Abfrage abzudecken, ohne dass ein Columnstore-Index erforderlich ist.
quelle
Ich glaube, das kann einfach so gemacht werden. Dies muss möglicherweise angepasst werden, aber Sie können einfach das Maximum aus der Gruppe auswählen.
Diese Antworten sind übertrieben.
quelle
In Szenarien, in denen Sie die Verwendung von row_count () vermeiden möchten, können Sie auch einen linken Join verwenden:
Für das Beispielschema können Sie auch eine "Nicht in Unterabfrage" verwenden, die im Allgemeinen mit derselben Ausgabe wie der linke Join kompiliert wird:
Beachten Sie, dass das Unterabfragemuster nicht funktionieren würde, wenn die Tabelle nicht mindestens einen einspaltigen eindeutigen Schlüssel / eine Einschränkung / einen Index hätte, in diesem Fall den Primärschlüssel "Id".
Diese beiden Abfragen sind in der Regel "teurer" als die Abfrage row_count () (gemessen mit Query Analyzer). Es kann jedoch vorkommen, dass Szenarien schneller Ergebnisse liefern oder andere Optimierungen ermöglichen.
quelle
quelle
Versuche dies:
quelle
Dies ist die Vanille-TSQL, die ich mir vorstellen kann
quelle
In SQLite wird überprüft, ob Sie die folgende einfache Abfrage mit GROUP BY verwenden können
Hier hilft MAX , das maximale DateCreated FROM jeder Gruppe zu erhalten.
Es scheint jedoch, dass MYSQL keine * -Spalten mit dem Wert von max DateCreated :( verknüpft
quelle