Wie erhalte ich eindeutige Werte für Nicht-Schlüsselspaltenfelder in Laravel?

92

Dies mag recht einfach sein, hat aber keine Ahnung, wie.

Ich habe eine Tabelle, die wiederholte Werte für ein bestimmtes Nicht-Schlüsselspaltenfeld haben kann. Wie schreibe ich eine SQL-Abfrage mit Query Builder oder Eloquent, die Zeilen mit unterschiedlichen Werten für diese Spalte abruft?

Beachten Sie, dass ich diese Spalte nicht nur abrufe, sondern in Verbindung mit anderen Spaltenwerten, sodass sie distinct()möglicherweise nicht wirklich funktioniert. Diese Frage kann also im Grunde sein, wie die Spalte angegeben werden soll, die in einer Abfrage, distinct()die keine Parameter akzeptiert, unterschieden werden soll.

gthuo
quelle

Antworten:

110

Sie sollten verwenden groupby. In Query Builder können Sie dies folgendermaßen tun:

$users = DB::table('users')
            ->select('id','name', 'email')
            ->groupBy('name')
            ->get();
Marcin Nabiałek
quelle
2
Ich habe eine Tabelle "Nachrichten" (für einen Chat verwendet) und möchte die neueste Nachricht erhalten, die der authentifizierte Benutzer von jeder Konversation erhalten hat. Mit groupBy kann ich nur eine Nachricht erhalten, aber ich bekomme die erste und ich brauche die letzte Nachricht. Scheint, dass orderBy nicht funktioniert.
JCarlosR
10
Wenn Sie keine mathematischen Operationen wie SUM, AVG, MAX usw. anwenden möchten, ist "Gruppieren nach" nicht die richtige Antwort (Sie können sie verwenden, sollten sie aber nicht verwenden). Sie sollten verschiedene verwenden. In MySQL können Sie DISTINCT für eine Spalte verwenden und der Ergebnismenge weitere Spalten hinzufügen: "SELECT DISTINCT Name, ID, E-Mail von Benutzern". Mit eloquent können Sie dies mit $ this-> select (['name', 'id', 'email']) -> unique () -> get () tun;
Sergi
1
where()Wie kann ich das beheben, wenn ich ein Problem hinzufüge ?
14.
1
Wenn ich dies benutze, wird angezeigt, dass 'id' und 'email' nicht in groupBy () enthalten sind. Ich muss groupBy ('id', 'name', 'email') verwenden. Welches ist nicht nützlich.
Haque
1
Etwas zu beachten: group byist nicht dasselbe wie distinct. group byändert das Verhalten von Aggregatfunktionen wie count()in der SQL-Abfrage. distinctDas Verhalten solcher Funktionen wird nicht geändert, sodass Sie je nach verwendeter Funktion unterschiedliche Ergebnisse erhalten.
Skeets
72

In Eloquent können Sie auch Folgendes abfragen:

$users = User::select('name')->distinct()->get();

Pathros
quelle
11
Frage speziell fragtNote that I am not fetching that column only, it is in conjunction with other column values
Hirnhamster
11
@Hirnhamster: Okkei. Diese Antwort ist jedoch immer noch nützlich für andere, die vorbeikommen ...
Pathros
3
Nicht nützlich für mich, ich schaue speziell, was das OP ist. Es ist kein einfacher Fund.
Blamb
1
$users = User::select('column1', 'column2', 'column3')->distinct()->get();
Mark-Hero
Auch benötigen Sie, ->orderBy('name')wenn Sie dies mit verwenden möchten chunk.
Chibueze Opata
25

in eloquent können Sie dies verwenden

$users = User::select('name')->groupBy('name')->get()->toArray() ;

groupBy ruft tatsächlich die unterschiedlichen Werte ab. Tatsächlich kategorisiert groupBy dieselben Werte, sodass wir Aggregatfunktionen für sie verwenden können. In diesem Szenario haben wir jedoch keine Aggregatfunktionen. Wir wählen nur den Wert aus, der dazu führt, dass das Ergebnis unterschiedliche Werte aufweist

Salar
quelle
4
Wie funktioniert das? Ruft die groupBy tatsächlich unterschiedliche Benutzernamen ab? Könnten Sie bitte etwas näher erläutern, wie dies das Problem von op löst?
Félix Gagnon-Grenier
Ja, groupBy ruft tatsächlich die unterschiedlichen Werte ab. Tatsächlich kategorisiert groupBy dieselben Werte, sodass wir Aggregatfunktionen für sie verwenden können. In diesem Szenario haben wir jedoch keine Aggregatfunktionen. Wir wählen nur den Wert aus, der dazu führt, dass das Ergebnis unterschiedliche Werte aufweist.
Salar
Ich verstehe ... wie unterscheidet sich diese Antwort von Marcins? Eine andere Antwort zu haben ist in Ordnung, solange Sie erklären können, wie es anders ist und welchen Fehler es löst :) Beantworten Sie keine Kommentare, sondern bearbeiten Sie Ihre Frage direkt mit relevanten Informationen.
Félix Gagnon-Grenier
1
Das Ergebnis ist dasselbe. Es hängt von Ihrem Projekt ab, ORM oder den Abfrage-Generator zu verwenden. Wenn Sie eines davon verwenden, ist es besser, sich an dieses zu halten. Deshalb habe ich Ihre Frage anders beantwortet.
Salar
Nun, ich habe dieses Problem, bei dem, wenn Sie jetzt mithilfe der in_array()Funktion überprüfen möchten, ob ein Element vorhanden ist , es nie funktioniert. Um das Problem zu beheben, habe ich ->lists()stattdessen versucht (Version 5.2). Also, $users = User::select('name')->groupBy('name')->lists('name');funktionierte gut für PHPs in_array();
Pathros
19

Obwohl ich zu spät komme, um darauf zu antworten, wäre es besser, mit Eloquent eindeutige Aufzeichnungen zu erhalten

$user_names = User::distinct()->get(['name']);
rsakhale
quelle
Ok, Sie haben einen Mehrwert geschaffen, indem Sie uns gezeigt haben, dass man auch Parameter von Feldnamen an die get()Methode übergeben kann (und ich habe auch die first()Methode getestet ), was der Verwendung der select()Methode entspricht, wie in einigen Antworten hier gezeigt. Obwohl irgendwie groupByimmer noch die höchste Punktzahl zu erzielen scheint. Dies wäre jedoch wirklich eindeutig, wenn dies die einzige Spalte ist, die ich auswähle.
Gthuo
In Bezug auf die Leistung wird eine eindeutige Bewertung gegenüber groupBy erzielt, da die Datensätze zuerst in der SQL Engine ausgewählt werden, anders als in Group By. Die Engine wählt zuerst alle Datensätze und dann die Gruppe nach ..
rsakhale
1
$user_names = User::distinct()->count(['name']);funktioniert auch
Bira
9

Das Gruppieren nach funktioniert nicht, wenn die Datenbankregeln nicht zulassen, dass sich eines der Auswahlfelder außerhalb einer Aggregatfunktion befindet. Verwenden Sie stattdessen die Laravel-Sammlungen .

$users = DB::table('users')
        ->select('id','name', 'email')
        ->get();

foreach($users->unique('name') as $user){
  //....
}

Jemand wies darauf hin, dass dies für große Sammlungen möglicherweise nicht besonders leistungsfähig ist. Ich würde empfehlen, der Sammlung einen Schlüssel hinzuzufügen. Die zu verwendende Methode heißt keyBy . Dies ist die einfache Methode.

     $users = DB::table('users')
        ->select('id','name', 'email')
        ->get()
        ->keyBy('name');

Mit dem keyBy können Sie auch eine Rückruffunktion für komplexere Dinge hinzufügen ...

     $users = DB::table('users')
        ->select('id','name', 'email')
        ->get()
        ->keyBy(function($user){
              return $user->name . '-' . $user->id;
         });

Wenn Sie große Sammlungen durchlaufen müssen, können Sie das Leistungsproblem beheben, indem Sie einen Schlüssel hinzufügen.

Jed Lynch
quelle
Sie haben Recht und sind genau das Problem, mit dem ich konfrontiert bin. Es ist jedoch auch nicht ideal, zu warten, bis die Abfrage ausgeführt wird und die Aktion in der Sammlung ausgeführt wird. Insbesondere wenn Sie sich auf die Paginierung verlassen, erfolgt Ihre Operation nach der Paginierungsberechnung, und die Elemente pro Seite sind falsch. Außerdem ist die Datenbank besser geeignet, um große Datenblöcke zu bearbeiten, als sie im Code abzurufen und zu verarbeiten. Für kleine
Datenblöcke
Du hast einen guten Punkt. Diese Methode ist in Bezug auf große Sammlungen möglicherweise nicht besonders leistungsfähig, und dies funktioniert nicht für die Paginierung. Ich denke, es gibt eine bessere Antwort als das, was ich habe.
Jed Lynch
5

Beachten Sie, dass groupBydie oben verwendete Funktion für Postgres nicht funktioniert.

Die Verwendung distinctist wahrscheinlich eine bessere Option - z $users = User::query()->distinct()->get();

Wenn Sie verwenden query, können Sie alle Spalten wie gewünscht auswählen.

jec006
quelle
5

$users = User::select('column1', 'column2', 'column3')->distinct()->get();Ruft alle drei Coulmns für unterschiedliche Zeilen in der Tabelle ab. Sie können beliebig viele Spalten hinzufügen.

Mark-Hero
quelle
5

** **.

Getestet für Laravel 5.8

** **.

Da Sie alle Spalten aus der Tabelle abrufen möchten, können Sie alle Daten erfassen und anschließend mithilfe der Funktion "Sammlungen" mit dem Namen " Einzigartig" filtern

// Get all users with unique name
User::all()->unique('name')

oder

// Get all & latest users with unique name 
User::latest()->get()->unique('name')

Weitere Informationen finden Sie in den Laravel Collection-Dokumentationen

BEARBEITEN: Möglicherweise haben Sie Probleme mit der Leistung. Wenn Sie Unique () verwenden, werden alle Daten zuerst aus der Benutzertabelle abgerufen und dann von Laravel gefiltert. Dieser Weg ist nicht gut, wenn Sie viele Benutzerdaten haben. Sie können den Abfrage-Generator verwenden und jedes Feld aufrufen, das Sie verwenden möchten. Beispiel:

User::select('username','email','name')->distinct('name')->get();
Robby Alvian Jaya Mulia
quelle
Dies ist nicht effizient, da Sie alle Daten zuerst von db
Razor Mureithi
Aus diesem Grund wird es als Filterung bezeichnet. Sie können für den Abfrage-Generator eindeutig () verwenden, aber Sie können keine anderen Felder abrufen (trotzdem können Sie jedes Feld über select aufrufen). Die Verwendung von unique () ist die einzige Möglichkeit, alle Felder abzurufen, ohne sie aufzurufen (obwohl wir wissen, dass dies Probleme mit der Leistung haben könnte).
Robby Alvian Jaya Mulia
2

Ich fand, dass diese Methode (für mich) ziemlich gut funktioniert, um eine flache Reihe eindeutiger Werte zu erzeugen:

$uniqueNames = User::select('name')->distinct()->pluck('name')->toArray();

Wenn Sie ->toSql()diesen Abfrage-Generator ausgeführt haben, wird eine Abfrage wie die folgende generiert:

select distinct `name` from `users`

Das ->pluck()wird von illuminate \ collection lib behandelt (nicht über eine SQL-Abfrage).

Latheesan
quelle
1

Ich hatte die gleichen Probleme beim Versuch, eine Liste aller eindeutigen Threads zu füllen, die ein Benutzer mit anderen Benutzern hatte. Das hat den Trick für mich getan

Message::where('from_user', $user->id)
        ->select(['from_user', 'to_user'])
        ->selectRaw('MAX(created_at) AS last_date')
        ->groupBy(['from_user', 'to_user'])
        ->orderBy('last_date', 'DESC')
        ->get()
Alex Christodoulou
quelle
0
$users = Users::all()->unique('name');
Roland Verner
quelle
5
Bitte erläutern Sie, wie Ihr Code-Snippet das Problem des OP löst.
ifconfig
0

Für diejenigen, die mögen, dass ich den gleichen Fehler mache. Hier ist die ausgearbeitete Antwort, die in Laravel 5.7 getestet wurde

A. Datensätze in der DB

UserFile :: orderBy ('created_at', 'desc') -> get () -> toArray ();

Array
(
    [0] => Array
        (
            [id] => 2073
            [type] => 'DL'
            [url] => 'https://i.picsum.photos/12/884/200/300.jpg'
            [created_at] => 2020-08-05 17:16:48
            [updated_at] => 2020-08-06 18:08:38
        )

    [1] => Array
        (
            [id] => 2074
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [created_at] => 2020-08-05 17:20:06
            [updated_at] => 2020-08-06 18:08:38
        )

    [2] => Array
        (
            [id] => 2076
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [created_at] => 2020-08-05 17:22:01
            [updated_at] => 2020-08-06 18:08:38
        )

    [3] => Array
        (
            [id] => 2086
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [created_at] => 2020-08-05 19:22:41
            [updated_at] => 2020-08-06 18:08:38
        )
)

B. Gewünschtes gruppiertes Ergebnis

UserFile :: select ('Typ', 'URL', 'Aktualisiertes_at) -> Unterscheidbar (' Typ ') -> get () -> toArray ();

Array
(
    [0] => Array
        (
            [type] => 'DL'
            [url] => 'https://i.picsum.photos/12/884/200/300.jpg'
            [updated_at] => 2020-08-06 18:08:38 
        )

    [1] => Array
        (
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [updated_at] => 2020-08-06 18:08:38
        )
)

Übergeben Sie also nur die Spalten "select()", deren Werte gleich sind. Zum Beispiel : 'type','url'. Sie können weitere Spalten hinzufügen, sofern diese denselben Wert haben wie 'updated_at'.

Wenn Sie versuchen, zu übergeben "created_at"oder "id"zu übergeben "select()", erhalten Sie dieselben Datensätze wie A. Da sie für jede Zeile in der Datenbank unterschiedlich sind.

Santosh Kumar
quelle