Bagaimana cara mendapatkan nilai yang berbeda untuk bidang kolom non-kunci di Laravel?


94

Ini mungkin cukup mudah tetapi tidak tahu caranya.

Saya memiliki tabel yang dapat memiliki nilai berulang untuk bidang kolom non-kunci tertentu. Bagaimana cara menulis kueri SQL menggunakan Query Builder atau Eloquent yang akan mengambil baris dengan nilai berbeda untuk kolom itu?

Perhatikan bahwa saya tidak mengambil kolom itu saja, ini berhubungan dengan nilai kolom lain, jadi distinct()mungkin tidak benar-benar berfungsi. Jadi pertanyaan itu pada dasarnya adalah bagaimana menentukan kolom yang ingin saya bedakan dalam kueri sekarang yang distinct()tidak menerima parameter?

Jawaban:


116

Anda harus menggunakan groupby. Di Query Builder, Anda dapat melakukannya dengan cara ini:

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

2
Saya memiliki tabel "pesan" (digunakan untuk mengobrol), dan saya ingin mendapatkan pesan terbaru yang telah diterima pengguna terotentikasi dari setiap percakapan. Menggunakan groupBy saya bisa mendapatkan hanya satu pesan, tapi saya mendapatkan yang pertama, dan saya membutuhkan pesan terakhir. Tampaknya orderBy tidak berfungsi.
JCarlosR

10
jika Anda tidak ingin menerapkan operasi matematika apa pun seperti SUM, AVG, MAX, dan seterusnya, "kelompokkan menurut" bukanlah jawaban yang benar (Anda dapat menggunakannya, tetapi Anda tidak boleh menggunakannya). Anda harus menggunakan berbeda. Di MySQL Anda dapat menggunakan DISTINCT untuk kolom dan menambahkan lebih banyak kolom ke set hasil: "PILIH DISTINCT nama, id, email DARI pengguna". Dengan eloquent Anda dapat melakukan ini dengan $ this-> pilih (['name', 'id', 'email']) -> different () -> get ();
Sergi

1
ketika saya menambahkan where()itu rusak, bagaimana cara memperbaikinya?
tq

1
ketika saya menggunakan ini, itu menunjukkan 'id' dan 'email' tidak ada di groupBy () saya perlu menggunakan groupBy ('id', 'name', 'email'). Yang mana tidak berguna.
Haque

1
Sesuatu yang perlu diperhatikan: group bytidak sama dengan distinct. group byakan mengubah perilaku fungsi agregat seperti count()di dalam kueri SQL. distincttidak akan mengubah perilaku fungsi tersebut, jadi Anda mendapatkan hasil yang berbeda bergantung pada yang mana yang Anda gunakan.
Skeets

78

Di Eloquent Anda juga dapat melakukan kueri seperti ini:

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


12
Pertanyaan secara khusus diajukanNote that I am not fetching that column only, it is in conjunction with other column values
Hirnhamster

12
@Hirnhamster: Okkei. Namun, jawaban ini masih berguna untuk orang lain yang lewat ...
Pathros

3
Tidak berguna bagi saya, saya secara khusus mencari apa OP itu. Ini tidak mudah ditemukan.
blamb

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

Anda juga perlu ->orderBy('name')jika Anda ingin menggunakan ini dengan chunk.
Chibueze Opata

26

dengan fasih Anda bisa menggunakan ini

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

groupBy sebenarnya mengambil nilai yang berbeda, sebenarnya groupBy akan mengkategorikan nilai yang sama, sehingga kita bisa menggunakan fungsi agregat padanya. tetapi dalam skenario ini kami tidak memiliki fungsi agregat, kami hanya memilih nilai yang akan menyebabkan hasil memiliki nilai yang berbeda


4
Bagaimana cara kerjanya? Apakah groupBy benar-benar mengambil nama pengguna yang berbeda? Bisakah Anda menjelaskan sedikit lagi bagaimana ini menjawab masalah operasi?
Félix Gagnon-Grenier

Ya groupBy sebenarnya mengambil nilai yang berbeda, sebenarnya groupBy akan mengkategorikan nilai yang sama, sehingga kita bisa menggunakan fungsi agregat padanya. tetapi dalam skenario ini kami tidak memiliki fungsi agregat, kami hanya memilih nilai yang akan menyebabkan hasil memiliki nilai yang berbeda.
Salar

Begitu ya .. lalu, bagaimana jawaban ini berbeda dari jawaban Marcin? Memiliki jawaban yang berbeda tidak apa-apa, selama Anda bisa menjelaskan bagaimana perbedaannya dan apa kekurangannya solusinya :) Jangan repot-repot menjawab di komentar, silakan langsung edit pertanyaan Anda dengan informasi yang relevan.
Félix Gagnon-Grenier

1
hasilnya sama, itu tergantung pada proyek Anda untuk menggunakan ORM atau menggunakan pembuat kueri, jika Anda menggunakan salah satunya, maka lebih baik tetap menggunakan yang itu. itulah mengapa saya menjawab pertanyaan Anda dengan cara yang berbeda.
Salar

Nah, saya memiliki masalah ini di mana, jika Anda sekarang ingin memeriksa apakah suatu item ada dengan menggunakan in_array()fungsinya, itu tidak pernah berfungsi. Untuk memperbaikinya, saya mencoba ->lists()(Versi 5.2). Jadi, $users = User::select('name')->groupBy('name')->lists('name');bekerja dengan baik untuk php in_array();
Pathros

19

Meskipun saya terlambat menjawab ini, pendekatan yang lebih baik untuk mendapatkan rekaman yang berbeda menggunakan Eloquent adalah

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

Oke, Anda telah menambahkan nilai dengan menunjukkan kepada kami bahwa seseorang juga dapat meneruskan parameter nama bidang ke get()metode (dan saya juga telah menguji first()metode tersebut), yang setara dengan menggunakan select()metode seperti yang ditunjukkan dalam beberapa jawaban di sini. Padahal entah kenapa groupBymasih nampaknya skor tertinggi. Namun ini akan sangat berbeda jika itu adalah satu-satunya kolom yang saya pilih.
gthuo

Dari segi kinerja, perbedaan akan menghasilkan skor atas groupBy, karena catatan akan dipilih pada awalnya di SQL Engine tidak seperti di Group By mesin pertama memilih semua catatan dan kemudian kelompok oleh ..
rsakhale

1
$user_names = User::distinct()->count(['name']);juga bekerja
Bira

11

Pengelompokan menurut tidak akan berfungsi jika aturan database tidak mengizinkan salah satu bidang pilihan berada di luar fungsi agregat. Sebagai gantinya gunakan koleksi laravel .

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

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

Seseorang menunjukkan bahwa kinerja ini mungkin tidak bagus untuk koleksi besar. Saya akan merekomendasikan menambahkan kunci ke koleksi. Metode yang digunakan disebut keyBy . Ini adalah metode yang sederhana.

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

KeyBy juga memungkinkan Anda menambahkan fungsi panggilan balik untuk hal-hal yang lebih kompleks ...

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

Jika Anda harus mengulangi koleksi besar, menambahkan kunci untuk itu menyelesaikan masalah kinerja.


Anda benar dan merupakan masalah yang saya hadapi ... namun menunggu hingga kueri dijalankan dan melakukan tindakan dalam koleksi juga tidak ideal. Khususnya jika Anda mengandalkan pagination, operasi Anda akan dilakukan setelah penghitungan pagination dan item per halaman akan menjadi salah. Selain itu, DB lebih cocok untuk beroperasi pada potongan data yang besar daripada mengambil dan memprosesnya dalam kode. Untuk potongan data kecil, ini bukan masalah
ChronoFish

Kamu punya pendapat bagus. Metode ini mungkin bukan performa yang bagus untuk koleksi besar, dan ini tidak akan berfungsi untuk penomoran halaman. Saya pikir ada jawaban yang lebih baik dari apa yang saya miliki.
Jed Lynch

6

Perhatikan bahwa groupByseperti yang digunakan di atas tidak akan berfungsi untuk postgres.

Menggunakan distinctmungkin merupakan pilihan yang lebih baik - mis $users = User::query()->distinct()->get();

Jika Anda menggunakan queryAnda dapat memilih semua kolom seperti yang diminta.


6

**

Diuji untuk Laravel 5.8

**

Karena Anda ingin mendapatkan semua kolom dari tabel, Anda dapat mengumpulkan semua data dan kemudian memfilternya menggunakan fungsi Koleksi yang disebut Unik

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

atau

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

Untuk informasi lebih lanjut, Anda dapat memeriksa Dokumentasi Koleksi Laravel

EDIT: Anda mungkin memiliki masalah dengan kinerja, dengan menggunakan Unique () Anda akan mendapatkan semua data terlebih dahulu dari tabel Pengguna, dan kemudian Laravel akan memfilternya. Cara ini tidak baik jika Anda memiliki banyak data Pengguna. Anda dapat menggunakan pembuat kueri dan memanggil setiap bidang yang ingin Anda gunakan, contoh:

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

Ini tidak efisien karena Anda mendapatkan semua data terlebih dahulu dari db
Razor Mureithi

Itulah mengapa Ini disebut pemfilteran, Anda dapat menggunakan different () untuk pembuat kueri tetapi Anda tidak bisa mendapatkan bidang lain (bagaimanapun Anda masih dapat memanggil setiap bidang melalui pemilihan). Dengan menggunakan unique () adalah satu-satunya cara untuk mendapatkan semua bidang tanpa memanggil masing-masing (Meskipun kami tahu itu mungkin bermasalah dengan kinerja).
Robby Alvian Jaya Mulia

5

$users = User::select('column1', 'column2', 'column3')->distinct()->get();mengambil ketiga coulmn untuk baris yang berbeda dalam tabel. Anda dapat menambahkan kolom sebanyak yang Anda inginkan.


3

Saya menemukan metode ini bekerja dengan cukup baik (bagi saya) untuk menghasilkan array datar dengan nilai unik:

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

Jika Anda menjalankan ->toSql()pembuat kueri ini, Anda akan melihatnya menghasilkan kueri seperti ini:

select distinct `name` from `users`

Ini ->pluck()ditangani oleh illuminate \ collection lib (bukan melalui query sql).


1

Saya mengalami masalah yang sama saat mencoba mengisi daftar semua utas unik yang dimiliki pengguna dengan pengguna lain. Ini melakukan trik untuk saya

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

Bagi yang menyukai saya melakukan kesalahan yang sama. Berikut adalah jawaban yang diuraikan Diuji di Laravel 5.7

A. Catatan di DB

UserFile :: orderBy ('create_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. Hasil Kelompok yang Diinginkan

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

Jadi teruskan kolom-kolom itu saja "select()", yang nilainya sama. Sebagai contoh: 'type','url'. Anda dapat menambahkan lebih banyak kolom asalkan memiliki nilai yang sama 'updated_at'.

Jika Anda mencoba untuk lulus "created_at"atau "id"masuk "select()", maka Anda akan mendapatkan catatan yang sama dengan A. Karena berbeda untuk setiap baris di DB.


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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.