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.

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();

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();


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


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']);

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.


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.


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.


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();

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).


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()


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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.