Як отримати чіткі значення для неключових полів стовпців у Laravel?


94

Це може бути досить просто, але ми не маємо уявлення, як це зробити.

У мене є таблиця, яка може мати повторювані значення для певного не ключового поля стовпця. Як написати SQL-запит за допомогою Query Builder або Eloquent, який отримає рядки з різними значеннями для цього стовпця?

Зверніть увагу, що я не отримую лише цей стовпець, він поєднується з іншими значеннями стовпців, тому distinct()може насправді не працювати. Отже, це питання в основному може полягати в тому, як вказати стовпець, який я хочу відрізняти в запиті зараз, який distinct()не приймає жодних параметрів?

Відповіді:


116

Ви повинні використовувати groupby. У Query Builder ви можете зробити це таким чином:

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

2
У мене є таблиця "повідомлення" (використовується для чату), і я хочу отримати останнє повідомлення, яке аутентифікований користувач отримував від кожної розмови. Використання groupBy Я можу отримати лише одне повідомлення, але я отримую перше, і мені потрібно останнє повідомлення. Здається, порядок By не працює.
JCarlosR

10
якщо ви не хочете застосовувати будь-які математичні операції, такі як SUM, AVG, MAX тощо, "згрупувати за" - це не правильна відповідь (ви можете використовувати його, але не слід використовувати). Ви повинні використовувати чіткі. У MySQL ви можете використовувати DISTINCT для стовпця і додати більше стовпців до набору результатів: "ВИБЕРІТЬ ІМЯ ДИСТАНТИКА, ідентифікатор, електронна пошта від користувачів". За допомогою красномовства ви можете зробити це за допомогою $ this-> select (['name', 'id', 'email']) -> distinct () -> get ();
Сергі

2
коли я додаю a, where()він ламається, як я можу це виправити?
tq

1
коли я використовую це, він показує "id", а "email" відсутній у groupBy (), мені потрібно використовувати groupBy ("id", "name", "email"). Що не корисно.
Хаке,

1
Щось, на що слід звернути увагу: group byце не те саме, що distinct. group byзмінить поведінку сукупних функцій, як count()усередині запиту SQL. distinctне змінить поведінку таких функцій, тому ви отримуєте різні результати залежно від того, яку з них ви використовуєте.
Скікетс

78

У Eloquent ви також можете робити такі запити:

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


12
Питання конкретно задаєтьсяNote that I am not fetching that column only, it is in conjunction with other column values
Hirnhamster

12
@Hirnhamster: Okkei. Однак ця відповідь все ще корисна для інших, хто проходить повз ...
Патрос,

3
Мені це не корисно, я спеціально розглядаю, що таке OP. Це непроста знахідка.
blamb

1
$users = User::select('column1', 'column2', 'column3')->distinct()->get();
Mark-Hero

Також вам потрібно, ->orderBy('name')якщо ви хочете використовувати це з chunk.
Chibueze Opata

26

красномовним ви можете цим користуватися

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

groupBy насправді отримує різні значення, насправді groupBy класифікує ті самі значення, щоб ми могли використовувати на них агреговані функції. але в цьому сценарії у нас немає сукупних функцій, ми просто вибираємо значення, яке призведе до того, що результат матиме різні значення


4
Як це працює? Чи насправді groupBy отримує різні імена користувачів? Не могли б ви пояснити ще трохи, як це відповідає на проблему ОП?
Фелікс Ганьон-Греньє

Так, groupBy насправді отримує різні значення, насправді groupBy класифікує ті самі значення, щоб ми могли використовувати на них агреговані функції. але в цьому сценарії у нас немає сукупних функцій, ми просто вибираємо значення, яке призведе до того, що результат матиме різні значення.
Салар

Я розумію .. тоді, чим ця відповідь відрізняється від відповіді Марсіна? Отримати іншу відповідь - це нормально, якщо ви можете пояснити, чим вона відрізняється і який недолік усуває :) Не турбуйтеся відповідями в коментарях, будь ласка, відредагуйте своє запитання відповідною інформацією.
Фелікс Ганьон-Греньє,

1
результат однаковий, залежно від вашого проекту використовувати ORM або використовувати конструктор запитів, якщо ви використовуєте один із них, то краще дотримуватися цього. тому я відповів на ваше запитання по-іншому.
Салар

Ну, у мене така проблема, коли, якщо ви зараз хочете перевірити, чи існує елемент за допомогою in_array()функції, це ніколи не працює. Щоб це виправити, я спробував ->lists()замість цього (версія 5.2). Отже, $users = User::select('name')->groupBy('name')->lists('name');чудово працював для php in_array();
Патрос

19

Хоча я запізнююсь відповісти на це, кращим підходом буде отримання чітких записів за допомогою Eloquent

$user_names = User::distinct()->get(['name']);

Гаразд, ви додали значення, показавши нам, що ми також можемо передавати параметри імен полів get()методу (і я тестував також first()методу), що еквівалентно використанню select()методу, як показано в декількох відповідях тут. Хоча якимось чином groupByвсе ще здається найвищим. Однак це було б по-справжньому чітко, якщо це єдиний стовпець, який я вибираю.
gthuo

З точки зору продуктивності, виразний збирається забити більше GroupBy, так як записи будуть обрані перші в SQL Engine , в відміну від групи від двигуна спочатку вибирає всі записи , а потім групи по ..
rsakhale

1
$user_names = User::distinct()->count(['name']);також працює
Біра

11

Групування за не буде працювати, якщо правила бази даних не дозволяють жодному з полів вибору бути поза агрегованою функцією. Натомість використовуйте колекції laravel .

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

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

Хтось зазначив, що це може бути не найкращим для продуктивності великих колекцій. Я б рекомендував додати ключ до колекції. Використовуваний метод називається keyBy . Це простий метод.

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

KeyBy також дозволяє додавати функцію зворотного дзвінка для більш складних речей ...

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

Якщо вам доведеться переглядати великі колекції, додавши до них ключ, вирішіть проблему продуктивності.


Ви маєте рацію, і саме в цьому проблема. Зокрема, якщо ви покладаєтесь на пагінацію, ваша операція буде виконана після обчислення пагінації, а елементи на сторінці будуть неправильними. Крім того, БД краще підходить для роботи з великими фрагментами даних, а не для отримання та обробки їх у коді. Для невеликих фрагментів даних це було б менш
серйозною

У вас є хороший момент. Цей метод може не мати високої продуктивності для великих колекцій, і це не буде працювати для пагінації. Я думаю, що є краща відповідь на те, що я маю.
Jed Lynch

6

Зверніть увагу, що, groupByяк використано вище, не працюватиме для postgres.

Використання distinct- це, мабуть, кращий варіант - напр $users = User::query()->distinct()->get();

Якщо ви використовуєте, queryви можете вибрати всі стовпці відповідно до запиту.


6

**

Перевірено на Laravel 5.8

**

Оскільки ви хочете отримати всі стовпці з таблиці, ви можете зібрати всі дані, а потім відфільтрувати їх за допомогою функції Колекції під назвою Унікальний

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

або

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

Для отримання додаткової інформації ви можете ознайомитися з документацією до колекції Laravel

РЕДАГУВАТИ: Можливо, у вас виникли проблеми з виконанням, використовуючи Unique (), ви спочатку отримаєте всі дані з таблиці користувачів, а потім Laravel відфільтрує їх. Цей спосіб поганий, якщо у вас багато даних про користувачів. Ви можете використовувати конструктор запитів і викликати кожне поле, яке ви хочете використовувати, наприклад:

User::select('username','email','name')->distinct('name')->get();

Це не ефективно, оскільки всі дані ви отримуєте спочатку з db
Razor Mureithi

Ось чому Це називається фільтрацією, ви можете використовувати distinct () для конструктора запитів, але ви не можете отримати інші поля (у будь-якому випадку ви все одно можете викликати кожне поле через select). Використання унікального () - це єдиний спосіб отримати всі поля без виклику кожного з них (Хоча ми знали, що це може мати проблеми з виконанням).
Роббі Алвіан Джая Мулія

5

$users = User::select('column1', 'column2', 'column3')->distinct()->get();отримує всі три сукупності для окремих рядків у таблиці. Ви можете додати скільки завгодно стовпців.


3

Я виявив, що цей метод працює досить добре (для мене) для створення плоского масиву унікальних значень:

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

Якщо ви працювали ->toSql()на цьому конструкторі запитів, ви побачите, що він генерує такий запит:

select distinct `name` from `users`

->pluck()Обробляється ILLUMINATE \ Lib збору (не через SQL - запит).


1

У мене були ті самі проблеми при спробі заповнити список усіх унікальних потоків, які користувач мав, з іншими користувачами. Це зробило для мене фокус

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

Для тих, хто любить мене, роблю ту ж помилку. Ось розроблена відповідь, перевірена в Laravel 5.7

А. Записи в БД

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. Бажаний згрупований результат

UserFile :: select ('type', 'url', 'updated_at) -> distinct (' type ') -> 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
        )
)

Тож передайте лише ті стовпці "select()", значення яких однакові. Наприклад: 'type','url'. Ви можете додати більше стовпців за умови, що вони мають таке саме значення 'updated_at'.

Якщо ви намагаєтеся пройти "created_at"або "id"в "select()", то ви отримаєте записи такий же , як А. Так як вони різні для кожного рядка в БД.


0
// Get unique value for table 'add_new_videos' column name 'project_id'
$project_id = DB::table('add_new_videos')->distinct()->get(['project_id']);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.