SQL MySQL groupBy + orderBy Letzten Einträge

-Rayz-

Lieutenant
Dabei seit
Okt. 2010
Beiträge
671
Hallo,

ich versuche hier mit Laravel die letzten 5 Einträge zu bekommen und das ohne doppelte Werte.
Code:
$ids = exampleTable::where('from_user', $userid)
            ->orderBy('created_at', 'DESC')
            ->distinct('to_user')
            ->get('to_user')->take(5);
In Kombination mit distinct funktioniert die Geschichte aber nicht. Klar könnt ich mir einfach alle Einträge holen und den Kram in php dann zurechtschneiden aber ich würde das schon gerne über die Eloquents lösen wollen.

Im Anhang hab ich mal ein Beispiel der Tabelle und das Ergebnis vom Query.

Als Ergebnis brauche ich halt die letzten 5 unterschiedlichen Einträge (to_user).
Im Ergebnis sieht man, dass direkt der erste Eintrag "d21fb3e9-f72c-4c05-aa2f-ce35c88730fd" nicht aufgelistet wird.
GroupBy ändert auch nichts daran.

Vielen Dank schon mal :-)
 

Anhänge

Tokolosh

Lieutenant
Dabei seit
März 2013
Beiträge
764
Ok aber jetzt die Frage:
Warum möchtest du das unbedingt in SQL lösen? Proof of concept? Ja SQL ist schnell und mächtig, ABER wenn es dir an diesen stellen Probleme bereitet, warum dann nicht auf PHP zurückgreifen, für das es in Laravel auch viele Helfermethoden für eben diese Datenmanipulation gibt. Vor allem bist du ja darauf angewiesen wie Eloquent das handhabt. Würdest du deinen eigenen Query schreiben müssen, wäre das was anderes, aber hier geht es ja explizit un Laravel/Eloquent, deshalb mach dir nicht zu viele Gedanken darum die Werte bis zum Ende heruntergebrochen aus Eloquent zu holen und mach den Rest mit den Laravel-Möglichkeiten. PHP ist zudem zuletzt so so viel besser/schneller geworden...

Ich finde, wenn du ein ORM verwendest, hast du dich für den nicht SQL weg entschieden irgendwie.
 

muesler

Commander
Dabei seit
Apr. 2005
Beiträge
2.099
LAST oder TOP sind keine Optionen? Oder noch nicht probiert?
 

-Rayz-

Lieutenant
Ersteller dieses Themas
Dabei seit
Okt. 2010
Beiträge
671
Oh man da hätte ich mich besser ausdrücken sollen... ich schreib das natürlich mit den Eloquents aber da sich die meisten eher mit Mysql auskennen als mit Laravel hab ich die Frage halt aufs MySQL bezogen.
Wie du aber an meinem Code siehst, mach ich es bereits ja über Laravel.

@muesler Latest hab ich über Laravel bereits probiert. Das änderte aber nichts
 

Andreas_

Commander
Dabei seit
Jan. 2017
Beiträge
2.078
Wenn Du die Frage auf MySQL beziehst, dann solltest Du auch das Statement angeben, wie es bei MySQL ankommt, also die Ausgabe von toSQL() oder von getQueryLog().

So wie Du das Problem beschreibst, ist es ein Laravel Problem.
 

-Rayz-

Lieutenant
Ersteller dieses Themas
Dabei seit
Okt. 2010
Beiträge
671
Mir ist es halt egal ob die Lösung in MySQL oder Laravel ist. Ob ich nun ein distinct->take(5) oder group by und limit nutze ist mir gleich.
Bekomm ich eine Lösung in MySQL schreib ich es halt in Laravel Eloquent um
 

Tokolosh

Lieutenant
Dabei seit
März 2013
Beiträge
764
In Kombination mit distinct funktioniert die Geschichte aber nicht. Klar könnt ich mir einfach alle Einträge holen und den Kram in php dann zurechtschneiden aber ich würde das schon gerne über die Eloquents lösen wollen.
Und du hast mich falsch verstanden...
Genau das war meine Frage. Warum machst du das auf biegen und brechen über die Datenbank? Deswegen sag ich ja: Wenn du dich fürr ein ORM entschieden hast, in dem Fall Eloquent, dann musst du damit rechnen, dass du dir viele Daten holen musst und diese mit PHP weiterverarbeiten musst. Mit PHP meine ich die Laravel Boardmitteln.
Ich spiele in diesem fall auf die Laravel Collections an, die genau dafür gedacht sind! Du wirst ja wohl nicht millionen Datensätze haben...
 

RalphS

Lt. Commander
Dabei seit
Feb. 2015
Beiträge
1.308
Ist das so ein scheiß Tool, das es anders nicht geht? Eine Datenbank fängt bei einer Mio Einträge doch erst an!

Ich kenn jetzt laravel nicht wirklich, würde aber intuitiv sagen, daß distinct Blödsinn ist.

Benötigt wird where from*= X AND to*= Y, order by created* in umgekehrter Reihenfolge, sodaß die letzten fünf Matches zuerst kommen.

Distinct sagt nur, daß wenn aus der gewählten Ergebnismenge doppelte Einträge drin sind, der Rest eliminiert wird. Wenn man also zu dieser Abfrage SELECT DISTINCT to* sagt, dann gibt es erwartungsgemäß immer entweder kein oder exakt ein Ergebnis.

Group by würde hier vermutlich nur mit MySQL keinen Fehler werden, da es kein Aggregat gibt und gemäß ursprünglicher Frage auch keins geben kann. Was geht wäre eine Windowfunktion, die ein Ranking setzt auf alle userids mit zugehörigem to* nach Ordnung created* desc. In einem zweiten Schritt würde man dann alle Rankings größer 5 rauswerfen.
SQL kann das zumindest verschachtelt. Bei Laravel müßte man gucken, ob / wie.
 
Zuletzt bearbeitet:

Andreas_

Commander
Dabei seit
Jan. 2017
Beiträge
2.078
Bekomm ich eine Lösung in MySQL schreib ich es halt in Laravel Eloquent um
Für eine Lösung in MySQL wäre es halt hilfreich, wenn Du Dein bisheriges SQL-Statement auch angeben würdest. Ich setze mich jetzt nicht hin und schaue, was denn bei
Code:
$ids = exampleTable::where('from_user', $userid)
            ->orderBy('created_at', 'DESC')
            ->distinct('to_user')
            ->get('to_user')->take(5);
am Ende für ein SQL herauskommt. Eventuell sieht man am fertigen SQL auch sofort, was am Laravel-Code falsch ist ... Bilder von Tabellen und Ergebnissen sind auch nichts, was mich zur Hilfestellung animiert, da Du dem Helfer unnötigen Aufwand bescherst.

/edit: in MySQL würde ich mit GROUP BY und MIN arbeiten, nach dem MIN sortieren und dann LIMIT 5. Windowfunktionen sind nicht wirklich nötig.
/edit2: Ich hatte da, den Laravel-Code wohl missverstanden, Statement korrigiert ... wäre nicht passiert, wenn das SQL-Statement und nicht nur der Laravel-Code geliefert worden wäre ...

Also so etwas:

SQL:
SELECT to_user, MIN(created_at) as created
FROM exampleTable
WHERE from_user = $userid
GROUP BY to_user
ORDER BY created DESC
LIMIT 5
Du hast da zwar eine Spalte mehr, als Du beim Ergebnis haben wolltest, aber es sortiert sich halt schlecht nach einer Spalte, die man nicht selektiert ...
 
Zuletzt bearbeitet:

RalphS

Lt. Commander
Dabei seit
Feb. 2015
Beiträge
1.308
Nach (fromUser, toUser) gruppieren okay, aber worauf dann die ersten fünf? Ich laß mich gern eines besseren belehren- keiner weiß “alles” und vielleicht hab ich das ursprüngliche Problem mißverstanden. Nach meinem Verständnis: die letzten fünf per toUser. Dafür die Windowfunktion.
Wenn es natürlich einfach absolut die letzten fünf Ergebnisse sein sollen... müßte group by fromUse, toUser reichen.
 

Andreas_

Commander
Dabei seit
Jan. 2017
Beiträge
2.078
fromUser = $userid ist die WHERE-Bedingung ... hatte ich übersehen ... werde mich beim nächsten Laravel-Problem raushalten.

Er will nicht wirklich die letzten fünf per toUser und auch nicht die absolut letzten fünf. Er will die letzten fünf (distinct) to_user. Wenn also die letzten fünf alle an denselben to_user gingen, zählt der trotzdem nur einmal. Deshalb GROUP BY to_user und min(created_at) zum Sortieren.
 
Zuletzt bearbeitet:

RalphS

Lt. Commander
Dabei seit
Feb. 2015
Beiträge
1.308
Hm... okay, gut möglich. Klingt mir nich sehr intuitiv als gewünschtes Ergebnis, und IMO ist der Op dann mißverständlich, aber möglich.

An der Stelle laufen wir womöglich in ein XY Problem.
 

Andreas_

Commander
Dabei seit
Jan. 2017
Beiträge
2.078
Naja, wenn es um irgendein Forum geht, will er vermutlich die letzten fünf User herausfinden mit denen der Betreffende Nachrichten ausgetauscht hat. Nachrichten an denselben User werden dann erst in der nächsten Darstellung aufgelistet (das wären dann die letzten Nachrichten pro to_user). Aber der TE hatte es ja nicht nötig, das ganze zu erläutern.
 

parats

Lt. Junior Grade
Dabei seit
Okt. 2018
Beiträge
384
Ok aber jetzt die Frage:
Warum möchtest du das unbedingt in SQL lösen? Proof of concept? Ja SQL ist schnell und mächtig, ABER wenn es dir an diesen stellen Probleme bereitet, warum dann nicht auf PHP zurückgreifen, für das es in Laravel auch viele Helfermethoden für eben diese Datenmanipulation gibt. Vor allem bist du ja darauf angewiesen wie Eloquent das handhabt. Würdest du deinen eigenen Query schreiben müssen, wäre das was anderes, aber hier geht es ja explizit un Laravel/Eloquent, deshalb mach dir nicht zu viele Gedanken darum die Werte bis zum Ende heruntergebrochen aus Eloquent zu holen und mach den Rest mit den Laravel-Möglichkeiten. PHP ist zudem zuletzt so so viel besser/schneller geworden...

Ich finde, wenn du ein ORM verwendest, hast du dich für den nicht SQL weg entschieden irgendwie.
Unabhängig von der eingesetzten Technologie sollte nur immer das über den Buffer gezogen werden, was auch wirklich benötigt wird. Gerade wenn "alles" nicht determenistisch ist, erzeugt man so ungewollt Last an stellen wo es keine geben sollte.
 

-Rayz-

Lieutenant
Ersteller dieses Themas
Dabei seit
Okt. 2010
Beiträge
671
So Guten Morgen, ich kam leider nicht eher dazu zu antworten.
Erstmal Sorry für die anscheinend unglückliche Fragestellung. Ich hatte mir eingebildet, dass es egal ist ob es nun Laravel oder MySQL ist da ich das eine auch ins andere übersetzen kann und umgekehrt. Auch das mein Laravel Eloquent recht eindeutig aufzeigt, was ich eigentlich für Daten hole. Die Tabellen im Anhang braucht man eigentlich gar nicht denn das Problem kann ja auf jede beliebige Tabelle angewandt werden.

Also ich versuch es mal zu erklären für was ich das brauche. Ich habe mehrere User im System und diese können sich untereinander Projekte sharen und damit diese nicht immer wieder erneut die E-Mail Adresse eingeben müssen,stelle ich eine Vorauswahl der letzten fünf unterschiedlichen E-Mail Adressen zur Verfügung an denen ein Projekt geteilt wurde.

Ich werde mal den Ansatz mit "min" testen. Komme nachher aber erst dazu.
Auf jedenfall schon mal vielen Dank und - trotz Montag - einen schönen Tag in die Woche :-)
 

-Rayz-

Lieutenant
Ersteller dieses Themas
Dabei seit
Okt. 2010
Beiträge
671
So geht es nun:
Code:
select `to_user`, MAX(created_at) as created_at
from `example`
where `from_user` = 'd1e3dfe7-ea6a-45a9-bff6-52f6b127809b'
group by `to_user`
order by `created_at` DESC
LIMIT 5
und in Laravel sieht es dann so aus:

PHP:
$result = example::select('to_user', DB::raw('MAX(created_at) as created'))
            ->where('from_user', $userid)
            ->groupBy('to_user')
            ->orderBy('created', 'DESC')
            ->get()
            ->take(5);
 
Top