2012-06-12 5 views
31

Sagen wir, ich habe eine Tabelle mit den Spalten genannt messages:Wie erhält man den letzten Datensatz in jeder Gruppe mit GROUP BY?

id | from_id | to_id | subject | message | timestamp 

ich die letzte Nachricht von jedem Benutzer nur erhalten möchten, wie Sie in Ihrem Facebook-Posteingang sehen würde, bevor Sie in den eigentlichen Faden nach unten bohren.

Diese Abfrage scheint mich nah, dass ich zu dem Ergebnis erhalten muß:

SELECT * FROM messages GROUP BY from_id 

jedoch die Abfrage mir die älteste Nachricht von jedem Benutzer zu geben und nicht die neuesten.

Ich kann das nicht herausfinden.

+0

Es gibt sogar eine bessere Lösung für dieses Problem [hier] (http://stackoverflow.com/questions/1313120/retrieving-the-last- Record-in-jeder-Gruppe) –

Antwort

74

Sie sollten letzte timestamp Werte in jeder Gruppe (Unterabfrage), herauszufinden, und dann diese Unterabfrage an den Tisch kommen -

SELECT t1.* FROM messages t1 
    JOIN (SELECT from_id, MAX(timestamp) timestamp FROM messages GROUP BY from_id) t2 
    ON t1.from_id = t2.from_id AND t1.timestamp = t2.timestamp; 
+4

+1 Ein häufiges Problem, aber vielen Dank, dass Sie MySQL nicht verwenden, um Spalten in der Auswahl zuzulassen, die nicht in der Gruppe enthalten sind! – GarethD

+0

Vielen Dank SOOO! Ich war in der Lage, zusätzliche Tabelle ohne Problem anzuschließen. So ein guter Ansatz. – user1019144

+0

Das ist einfach perfekt. – enchance

-4

Sie müssen sie bestellen.

SELECT * FROM messages GROUP BY from_id ORDER BY timestamp DESC LIMIT 1

+0

wählen top (1) * .... – BumbleB2na

+0

@ BumbleB2na 'LIMIT 1' meinst du? – Li0liQ

+0

jetzt erhalten Sie die letzte Nachricht von allen Nachrichten, Sie brauchen eine wo Bedingung – jcho360

1

Dies ist ein Standardproblem.

Beachten Sie, dass MySQL Ihnen erlaubt, Spalten aus der GROUP BY-Klausel wegzulassen, was Standard-SQL nicht tut, aber Sie erhalten im Allgemeinen keine deterministischen Ergebnisse, wenn Sie die MySQL-Funktion verwenden.

SELECT * 
    FROM Messages AS M 
    JOIN (SELECT To_ID, From_ID, MAX(TimeStamp) AS Most_Recent 
      FROM Messages 
     WHERE To_ID = 12345678 
     GROUP BY From_ID 
     ) AS R 
    ON R.To_ID = M.To_ID AND R.From_ID = M.From_ID AND R.Most_Recent = M.TimeStamp 
WHERE M.To_ID = 12345678 

Ich habe einen Filter auf dem To_ID hinzugefügt zu entsprechen, was Sie wahrscheinlich zu haben sind. Die Abfrage funktioniert ohne sie, aber wird im Allgemeinen viel mehr Daten zurückgeben. Die Bedingung sollte weder in der verschachtelten Abfrage noch in der äußeren Abfrage angegeben werden (der Optimierer sollte die Bedingung automatisch nach unten verschieben), aber es kann nicht schaden, die Bedingung wie gezeigt zu wiederholen.

+0

Die neueste SQL-Standard ermöglicht es Ihnen, Spalten in der 'GROUP BY' und schließen Sie sie in die' SELECT'- oder 'HAVING'-Klausel ein, sofern sie funktional von der' GROUP BY'-Kombination abhängig sind - und somit nur deterministische Ergebnisse zurückgegeben werden. (MySQL tut dies natürlich nicht.) –

+0

@ypercube Nicht sicher, ob dies der richtige Ort dafür ist, aber haben Sie irgendwelche guten Links dafür. Ich kann nicht verstehen, wie die Auswahl von Spalten, die nicht in der Gruppe sind, deterministisch werden kann, indem ich von Elementen in der Gruppe abhängig bin. Die einzige Möglichkeit, sie nichtdeterministisch zu machen, ist die Verwendung von Ordnung durch. Allerdings können Beispiele helfen, Dinge zu klären. Danke – GarethD

+0

'GROUP BY pk' wäre ein einfaches Beispiel. Meine Antwort [hier] (http://stackoverflow.com/questions/7594865/why-does-mysql-add-a-feature-that-conflicts-with-sql-standards/7596265#7596265) hat einen Link zu (eine Kopie von) dem Standard. –

2

Gerade Ergänzung, was Devart sagte, wird der Code unten nicht nach der Bestellung auf die Frage:

SELECT t1.* FROM messages t1 
    JOIN (SELECT from_id, MAX(timestamp) timestamp FROM messages GROUP BY from_id) t2 
    ON t1.from_id = t2.from_id AND t1.timestamp = t2.timestamp; 

Die „GROUP BY-Klausel“ in der Hauptabfrage da, dass wir den „SOURCE“ sein muss, müssen zuerst neu ordnen

SELECT t1.* FROM messages t1 
    JOIN (SELECT from_id, MAX(timestamp) timestamp FROM messages ORDER BY timestamp DESC) t2 
    ON t1.from_id = t2.from_id AND t1.timestamp = t2.timestamp GROUP BY t2.timestamp; 

Grüße,

12

Versuchen Sie, diese

: die benötigte "Gruppierung" so zu erhalten
SELECT * FROM messages where id in (SELECT max(id) FROM messages GROUP BY from_id) order by id desc 
+3

Während dieser Code die Frage beantworten kann, wäre es besser, etwas _context_ einzubeziehen, _how_ es funktioniert zu erklären und _when_, um es zu benutzen. Nur-Code-Antworten sind auf lange Sicht nicht nützlich. –

+0

"SELECT max (id) FROM Nachrichten GROUP BY von_id" diese innere Abfrage, zuerst gruppiert Datensätze/Nachrichten von Benutzer (from_id) dann ziehen max Datensatz-ID. Dann fragen wir erneut nach der Nachrichtentabelle, um nur die neuesten Datensätze/Nachrichten aus der inneren Abfrageergebnismenge zu erhalten. –

+0

Die einfachste Lösung IMHO – Nowdeen

2

diese Abfrage Rückkehr letzte Datensatz für jeden form_id:

SELECT m1.* 
    FROM messages m1 LEFT JOIN messages m2 
    ON (m1.Form_id = m2.Form_id AND m1.id < m2.id) 
    WHERE m2.id IS NULL; 
+0

Ehrlich gesagt ist diese Antwort unterbewertet. Dies war die einzige Lösung, die für mich gut funktionierte, da ich neben meinem Auto-Inkrement-Feld nach einem anderen Feld gruppiere, und ich muss das späteste Datum auswählen. –

+0

Diese Antwort hat mir geholfen, es mit Hibernate HQL arbeiten zu lassen. Keine der anderen Antworten funktionierte, weil Hibernate Unterabfragen nur nach WHERE und SELECT unterstützt. Da diese Antwort keine Unterabfragen verwendet, funktionierte es einwandfrei. – Alex