Es gibt viele ähnliche Fragen, die hier zu finden sind, aber ich denke nicht, dass jemand die Frage angemessen beantwortet.
Ich werde mit der derzeit beliebtesten Frage fortfahren und ihr Beispiel verwenden, wenn das in Ordnung ist.
In diesem Fall besteht die Aufgabe darin, den neuesten Beitrag für jeden Autor in der Datenbank abzurufen.
Die Beispielabfrage führt zu unbrauchbaren Ergebnissen, da nicht immer der letzte Beitrag zurückgegeben wird.
SELECT wp_posts.* FROM wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
ORDER BY wp_posts.post_date DESC
Die aktuell akzeptierte Antwort lautet
SELECT
wp_posts.*
FROM wp_posts
WHERE
wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC
Leider ist diese Antwort schlicht und einfach falsch und führt in vielen Fällen zu weniger stabilen Ergebnissen als die ursprüngliche Abfrage.
Meine beste Lösung besteht darin, eine Unterabfrage des Formulars zu verwenden
SELECT wp_posts.* FROM
(
SELECT *
FROM wp_posts
ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
Meine Frage ist dann einfach: Gibt es überhaupt eine Möglichkeit, Zeilen vor dem Gruppieren zu ordnen, ohne auf eine Unterabfrage zurückzugreifen?
Bearbeiten : Diese Frage war eine Fortsetzung einer anderen Frage und die Besonderheiten meiner Situation unterscheiden sich geringfügig. Sie können (und sollten) davon ausgehen, dass es auch eine wp_posts.id gibt, die eine eindeutige Kennung für diesen bestimmten Beitrag darstellt.
quelle
post_author
undpost_date
sind nicht genug, um eine eindeutige Zeile zu erhalten, daher muss es mehr geben, um eine eindeutige Zeile propost_author
There are plenty of similar questions to be found on here but I don't think that any answer the question adequately.
Dafür sind Kopfgelder da.Antworten:
Die Verwendung von
ORDER BY
in einer Unterabfrage ist nicht die beste Lösung für dieses Problem.Die beste Lösung, um den
max(post_date)
Autor zu ermitteln, besteht darin, eine Unterabfrage zu verwenden, um das maximale Datum zurückzugeben, und diese dann sowohl ampost_author
als auch am maximalen Datum mit Ihrer Tabelle zu verknüpfen .Die Lösung sollte sein:
Wenn Sie folgende Beispieldaten haben:
Die Unterabfrage gibt das maximale Datum und den Autor von zurück:
Da Sie dies dann wieder mit der Tabelle verknüpfen, geben Sie für beide Werte die vollständigen Details dieses Beitrags zurück.
Siehe SQL Fiddle mit Demo .
Um meine Kommentare zur Verwendung einer Unterabfrage zur genauen Rückgabe dieser Daten zu erweitern.
MySQL zwingt Sie nicht zu
GROUP BY
jeder Spalte, die Sie in dieSELECT
Liste aufnehmen. Wenn Sie also nurGROUP BY
eine Spalte verwenden, aber insgesamt 10 Spalten zurückgeben, gibt es keine Garantie dafür, dass die anderen Spaltenwerte, die zu denpost_author
zurückgegebenen gehören , zurückgegeben werden. Wenn sich die Spalte nicht inGROUP BY
MySQL befindet, wird ausgewählt, welcher Wert zurückgegeben werden soll.Durch die Verwendung der Unterabfrage mit der Aggregatfunktion wird sichergestellt, dass jedes Mal der richtige Autor und Beitrag zurückgegeben wird.
ORDER BY
Nebenbei bemerkt, während MySQL die Verwendung von a in einer Unterabfrage und die Anwendung von aGROUP BY
auf nicht jede Spalte in derSELECT
Liste ermöglicht, ist dieses Verhalten in anderen Datenbanken, einschließlich SQL Server, nicht zulässig.quelle
wp_posts
in beiden Spalten, um die vollständige Zeile zu erhalten.GROUP BY
zum einen nur auf eine Spalte anwenden , gibt es keine Garantie dafür, dass die Werte in den anderen Spalten konsistent korrekt sind. Leider lässt MySQL diese Art von SELECT / GROUPing zu, was bei anderen Produkten nicht der Fall ist.ORDER BY
Zweitens ist die Syntax der Verwendung von a in einer Unterabfrage, während sie in MySQL zulässig ist, in anderen Datenbankprodukten, einschließlich SQL Server, nicht zulässig. Sie sollten eine Lösung verwenden, die bei jeder Ausführung das richtige Ergebnis zurückgibt.INDEX(post_author, post_date)
wichtig.post_id
Ihre innere Abfrage eingeben, sollten Sie sich technisch auch danach gruppieren, was Ihre Ergebnisse höchstwahrscheinlich verzerren würde.Ihre Lösung verwendet eine Erweiterung der GROUP BY- Klausel, mit der Sie nach bestimmten Feldern gruppieren können (in diesem Fall nur
post_author
):und wählen Sie nicht aggregierte Spalten aus:
die nicht in der group by-Klausel aufgeführt sind oder die nicht in einer Aggregatfunktion (MIN, MAX, COUNT usw.) verwendet werden.
Richtige Verwendung der Erweiterung der GROUP BY-Klausel
Dies ist nützlich, wenn alle Werte nicht aggregierter Spalten für jede Zeile gleich sind.
Angenommen, Sie haben einen Tisch
GardensFlowers
(name
des Gartens, der im Gartenflower
wächst):und Sie möchten alle Blumen extrahieren, die in einem Garten wachsen, in dem mehrere Blumen wachsen. Dann müssen Sie eine Unterabfrage verwenden, zum Beispiel könnten Sie diese verwenden:
Wenn Sie stattdessen alle Blumen extrahieren müssen, die die einzigen Blumen im Garder sind, können Sie einfach die HAVING-Bedingung in ändern
HAVING COUNT(DISTINCT flower)=1
, aber MySql ermöglicht Ihnen auch Folgendes:Keine Unterabfrage, kein Standard-SQL, aber einfacher.
Falsche Verwendung der Erweiterung der GROUP BY-Klausel
Aber was passiert, wenn Sie nicht aggregierte Spalten auswählen, die nicht für jede Zeile gleich sind? Welchen Wert wählt MySql für diese Spalte?
Es sieht so aus, als würde MySQL immer den ERSTEN Wert auswählen, auf den es stößt.
Um sicherzustellen, dass der erste Wert, auf den er trifft, genau der gewünschte Wert ist, müssen Sie a
GROUP BY
auf eine geordnete Abfrage anwenden , daher muss eine Unterabfrage verwendet werden. Sie können es nicht anders machen.Unter der Annahme, dass MySql immer die erste Zeile auswählt, auf die es trifft, sortieren Sie die Zeilen vor der GROUP BY korrekt. Wenn Sie die Dokumentation jedoch sorgfältig lesen, werden Sie leider feststellen, dass diese Annahme nicht zutrifft.
Bei der Auswahl nicht aggregierter Spalten, die nicht immer gleich sind, kann MySql einen beliebigen Wert auswählen, sodass der resultierende Wert, der tatsächlich angezeigt wird, unbestimmt ist .
Ich sehe, dass dieser Trick, um den ersten Wert einer nicht aggregierten Spalte zu erhalten, häufig verwendet wird und normalerweise / fast immer funktioniert. Ich verwende ihn manchmal auch (auf eigenes Risiko). Da dies jedoch nicht dokumentiert ist, können Sie sich nicht auf dieses Verhalten verlassen.
Dieser Link (danke ypercube!) Der GROUP BY-Trick wurde weg optimiert. Er zeigt eine Situation, in der dieselbe Abfrage unterschiedliche Ergebnisse zwischen MySql und MariaDB zurückgibt, wahrscheinlich aufgrund einer anderen Optimierungs-Engine.
Wenn dieser Trick funktioniert, ist es nur eine Frage des Glücks.
Die akzeptierte Antwort auf die andere Frage erscheint mir falsch:
wp_posts.post_date
ist eine nicht aggregierte Spalte, und ihr Wert wird offiziell unbestimmt sein, aber es wird wahrscheinlich die erste sein, diepost_date
angetroffen wird. Da der GROUP BY-Trick jedoch auf eine ungeordnete Tabelle angewendet wird, ist nicht sicher, welche zuerstpost_date
angetroffen wird.Es werden wahrscheinlich Beiträge zurückgegeben, die die einzigen Beiträge eines einzelnen Autors sind, aber selbst dies ist nicht immer sicher.
Eine mögliche Lösung
Ich denke, dass dies eine mögliche Lösung sein könnte:
Bei der inneren Abfrage gebe ich das maximale Post-Datum für jeden Autor zurück. Ich berücksichtige dann die Tatsache, dass derselbe Autor theoretisch zwei Beiträge gleichzeitig haben könnte, sodass ich nur die maximale ID erhalte. Und dann gebe ich alle Zeilen zurück, die diese maximalen IDs haben. Es könnte schneller mit Joins anstelle der IN-Klausel gemacht werden.
(Wenn Sie sicher sind, dass dies
ID
nur zunimmt und diesID1 > ID2
auch bedeutet,post_date1 > post_date2
könnte die Abfrage viel einfacher gestaltet werden, aber ich bin mir nicht sicher, ob dies der Fall ist.)quelle
extension to GROUP By
ist eine interessante Lektüre, danke dafür.Was Sie lesen werden, ist ziemlich hackig, versuchen Sie es also nicht zu Hause!
In SQL lautet die Antwort auf Ihre Frage im Allgemeinen NEIN . Aufgrund des entspannten Modus von
GROUP BY
(von @bluefeet erwähnt ) lautet die Antwort in MySQL JA .Angenommen, Sie haben einen BTREE-Index für (post_status, post_type, post_author, post_date). Wie sieht der Index unter der Haube aus?
(post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer A', post_date = '2012-12-01') (post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer A', post_date = '2012-12-31') (post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer B', post_date = '2012-10-01') (post_status = 'veröffentlichen', post_type = ' post ', post_author =' Benutzer B ', post_date =' 2012-12-01 ')
Das heißt, die Daten werden nach all diesen Feldern in aufsteigender Reihenfolge sortiert.
Wenn Sie eine
GROUP BY
standardmäßigpost_author
ausführen, werden die Daten nach dem Gruppierungsfeld sortiert ( in unserem Fall sind post_status, post_type für dieWHERE
Klausel erforderlich ). Wenn ein übereinstimmender Index vorhanden ist, werden die Daten für jeden ersten Datensatz in aufsteigender Reihenfolge erfasst . Das heißt, die Abfrage ruft Folgendes ab (den ersten Beitrag für jeden Benutzer):(post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer A', post_date = '2012-12-01') (post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer B', post_date = '2012-10-01')
Aber
GROUP BY
in MySQL können Sie die Reihenfolge explizit angeben. Und wenn Siepost_user
in absteigender Reihenfolge anfordern , wird unser Index in umgekehrter Reihenfolge durchlaufen, wobei immer noch der erste Datensatz für jede Gruppe erstellt wird, die tatsächlich die letzte ist.Das ist
wird uns geben
(post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer B', post_date = '2012-12-01') (post_status = 'veröffentlichen', post_type = 'post', post_author = 'Benutzer A', post_date = '2012-12-31')
Wenn Sie nun die Ergebnisse der Gruppierung nach post_date ordnen, erhalten Sie die gewünschten Daten.
NB :
Dies ist nicht das, was ich für diese spezielle Abfrage empfehlen würde. In diesem Fall würde ich eine leicht modifizierte Version dessen verwenden , was @bluefeet vorschlägt. Aber diese Technik könnte sehr nützlich sein. Schauen Sie sich meine Antwort hier an: Abrufen des letzten Datensatzes in jeder Gruppe
Fallstricke : Die Nachteile des Ansatzes sind folgende
Der Vorteil ist die Leistung in schwierigen Fällen. In diesem Fall sollte die Leistung der Abfrage aufgrund der Datenmenge beim Sortieren dieselbe sein wie bei der Abfrage von @ bluefeet (alle Daten werden in eine temporäre Tabelle geladen und dann sortiert; übrigens erfordert seine Abfrage auch den
(post_status, post_type, post_author, post_date)
Index). .Was ich vorschlagen würde :
Wie gesagt, diese Abfragen führen dazu, dass MySQL Zeit damit verschwendet, potenziell große Datenmengen in einer temporären Tabelle zu sortieren. Falls Sie Paging benötigen (dh LIMIT ist beteiligt), werden die meisten Daten sogar verworfen. Was ich tun würde, ist die Menge der sortierten Daten zu minimieren: das heißt, ein Minimum an Daten in der Unterabfrage zu sortieren und zu begrenzen und dann wieder mit der gesamten Tabelle zu verbinden.
Dieselbe Abfrage unter Verwendung des oben beschriebenen Ansatzes:
Alle diese Abfragen mit ihren Ausführungsplänen in SQLFiddle .
quelle
Probier diese. Holen Sie sich einfach die Liste der neuesten Post-Daten von jedem Autor . Das ist es
quelle
post_date IN (select max(...) ...)
. Dies ist effizienter als das ErstellenIN ( SELECT ... )
ist viel weniger effizient als das entsprechende JOIN.Nein. Es ist nicht sinnvoll, die Datensätze vor dem Gruppieren zu ordnen, da durch Gruppieren die Ergebnismenge mutiert wird. Der Unterabfrageweg ist der bevorzugte Weg. Wenn dies zu langsam geht, müssen Sie Ihr Tabellendesign ändern, indem Sie beispielsweise die ID des letzten Beitrags für jeden Autor in einer separaten Tabelle speichern oder eine boolesche Spalte einfügen, die für jeden Autor angibt, welcher seiner Beiträge der letzte ist einer.
quelle
Verwenden Sie einfach die Max-Funktion und die Gruppenfunktion
quelle
Um es noch einmal zusammenzufassen: Die Standardlösung verwendet eine nicht korrelierte Unterabfrage und sieht folgendermaßen aus:
Wenn Sie eine alte Version von MySQL oder einen relativ kleinen Datensatz verwenden, können Sie die folgende Methode verwenden:
quelle
** Unterabfragen können sich bei Verwendung mit großen Datenmengen negativ auf die Leistung auswirken **
Ursprüngliche Abfrage
Geänderte Abfrage
Da ich
max
inselect clause
==> verwendemax(p.post_date)
, ist es möglich, Unterauswahlabfragen zu vermeiden und nach der Spalte max nach der Gruppe nach zu sortieren .quelle
Verwenden Sie * in select nicht, da dies die Leistung beeinträchtigt und die Verwendung der Gruppe nach und nach der Reihenfolge behindert. Versuchen Sie diese Abfrage:
Wenn Sie in ORDER BY nicht nur die Tabelle angeben, sondern nur den Alias, wird das Ergebnis der Auswahl sortiert.
quelle