วิธีรับค่าที่แตกต่างกันสำหรับฟิลด์คอลัมน์ที่ไม่ใช่คีย์ใน 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 ฉันได้รับข้อความเดียว แต่ฉันได้รับข้อความแรกและฉันต้องการข้อความสุดท้าย ดูเหมือนว่า orderBy ไม่ทำงาน
JCarlosR

10
หากคุณไม่ต้องการใช้การคำนวณทางคณิตศาสตร์เช่น SUM, AVG, MAX และอื่น ๆ "จัดกลุ่มตาม" ไม่ใช่คำตอบที่ถูกต้อง (คุณสามารถใช้ได้ แต่ไม่ควรใช้) คุณควรใช้ความแตกต่าง ใน MySQL คุณสามารถใช้ DISTINCT สำหรับคอลัมน์และเพิ่มคอลัมน์เพิ่มเติมในชุดผลลัพธ์: "เลือก DISTINCT name, id, อีเมลจากผู้ใช้" ด้วยฝีปากคุณสามารถทำได้ด้วย $ this-> select (['name', 'id', 'email']) -> unique () -> get ();
Sergi

2
เมื่อฉันเพิ่มwhere()มันแตกฉันจะแก้ไขได้อย่างไร
tq

1
เมื่อฉันใช้สิ่งนี้จะแสดงว่า 'id' และ 'email' ไม่ได้อยู่ใน groupBy () ฉันจำเป็นต้องใช้ groupBy ('id', 'name', 'email') ซึ่งไม่มีประโยชน์.
Haque

1
สิ่งที่ควรทราบ: group byไม่เหมือนกับdistinctไฟล์. group byจะเปลี่ยนลักษณะการทำงานของฟังก์ชันการรวมเช่นcount()ภายในแบบสอบถาม SQL distinctจะไม่เปลี่ยนการทำงานของฟังก์ชันดังกล่าวดังนั้นคุณจะได้ผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับว่าคุณใช้ฟังก์ชันใด
Skeets

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
@ เฮิร์นแฮมสเตอร์: Okkei. อย่างไรก็ตามคำตอบนี้ยังมีประโยชน์สำหรับคนอื่น ๆ ที่เดินผ่าน ...
Pathros

3
ไม่มีประโยชน์สำหรับฉันฉันมองเฉพาะว่า OP คืออะไร หาไม่ง่าย
ตำหนิ

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 ดึงชื่อผู้ใช้ที่แตกต่างกันจริงหรือไม่ คุณช่วยอธิบายเพิ่มเติมอีกเล็กน้อยได้ไหมว่าสิ่งนี้ตอบปัญหาของ op ได้อย่างไร
Félix Gagnon-Grenier

ใช่ groupBy กำลังดึงค่าที่แตกต่างออกไปในความเป็นจริง groupBy จะจัดหมวดหมู่ค่าเดียวกันเพื่อให้เราสามารถใช้ฟังก์ชันการรวมกับค่าเหล่านี้ได้ แต่ในสถานการณ์นี้เราไม่มีฟังก์ชันการรวมเราเพียงแค่เลือกค่าที่จะทำให้ผลลัพธ์มีค่าที่แตกต่างกัน
Salar

ฉันเข้าใจ .. แล้วคำตอบนี้ต่างจากคำตอบของมาร์ซินอย่างไร? การมีคำตอบที่แตกต่างออกไปก็ใช้ได้ตราบเท่าที่คุณสามารถอธิบายได้ว่ามันแตกต่างกันอย่างไรและมีข้อบกพร่องอะไรบ้าง :) อย่าตอบในความคิดเห็นโปรดแก้ไขคำถามของคุณโดยตรงด้วยข้อมูลที่เกี่ยวข้อง
Félix Gagnon-Grenier

1
ผลลัพธ์จะเหมือนกันขึ้นอยู่กับโปรเจ็กต์ของคุณที่จะใช้ ORM หรือใช้ตัวสร้างแบบสอบถามหากคุณใช้หนึ่งในนั้นจะดีกว่าที่จะยึดติดกับอันนั้น นั่นเป็นเหตุผลที่ฉันตอบคำถามของคุณด้วยวิธีอื่น
Salar

ฉันมีปัญหานี้โดยที่หากคุณต้องการตรวจสอบว่ามีรายการอยู่หรือไม่โดยใช้in_array()ฟังก์ชันนี้จะไม่ทำงาน ในการแก้ไขฉันพยายาม->lists()แทน (เวอร์ชัน 5.2) ดังนั้น$users = User::select('name')->groupBy('name')->lists('name');ทำงานได้ดีสำหรับ php in_array();
Pathros

19

แม้ว่าฉันจะตอบช้า แต่วิธีที่ดีกว่าในการรับบันทึกที่แตกต่างกันโดยใช้ Eloquent ก็น่าจะเป็น

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

โอเคคุณได้เพิ่มมูลค่าโดยการแสดงให้เราเห็นว่าเราสามารถส่งพารามิเตอร์ชื่อฟิลด์ไปยังget()เมธอดได้ (และฉันได้ทดสอบfirst()วิธีนี้ด้วย) ซึ่งเทียบเท่ากับการใช้select()วิธีการดังที่แสดงในคำตอบสองสามข้อที่นี่ แม้ว่าจะgroupByยังคงทำคะแนนสูงสุด อย่างไรก็ตามสิ่งนี้จะแตกต่างอย่างแท้จริงหากนั่นเป็นคอลัมน์เดียวที่ฉันเลือก
gthuo

ประสิทธิภาพที่ชาญฉลาดแตกต่างจะได้คะแนนเหนือ groupBy เนื่องจากระเบียนจะถูกเลือกในตอนแรกใน SQL Engine ซึ่งแตกต่างจาก Group By engine ก่อนจะเลือกระเบียนทั้งหมดจากนั้นจัดกลุ่มตาม ..
rsakhale

1
$user_names = User::distinct()->count(['name']);ยังใช้งานได้
Bira

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

หากคุณต้องทำซ้ำในคอลเลคชันขนาดใหญ่การเพิ่มคีย์จะช่วยแก้ปัญหาด้านประสิทธิภาพได้


คุณถูกต้องและเป็นปัญหาที่ฉันต่อต้าน ... อย่างไรก็ตามการรอจนกว่าการสืบค้นจะดำเนินการและดำเนินการในคอลเล็กชันก็ไม่เหมาะ โดยเฉพาะอย่างยิ่งถ้าคุณอาศัยการแบ่งหน้าการดำเนินการของคุณจะอยู่หลังการคำนวณเลขหน้าและรายการต่อหน้าจะผิด นอกจากนี้ DB ยังเหมาะกว่าที่จะทำงานกับชิ้นข้อมูลขนาดใหญ่แทนที่จะดึงและประมวลผลในโค้ด สำหรับชิ้นข้อมูลขนาดเล็กจะมีปัญหาน้อยกว่า
ChronoFish

คุณมีจุดที่ดี วิธีนี้อาจไม่ได้ประสิทธิภาพที่ยอดเยี่ยมในคอลเลคชันขนาดใหญ่และใช้ไม่ได้กับการแบ่งหน้า ฉันคิดว่ามีคำตอบที่ดีกว่าว่าสิ่งที่ฉันมี
Jed Lynch

6

โปรดทราบว่าgroupByตามที่ใช้ข้างต้นจะใช้ไม่ได้กับ postgres

การใช้distinctน่าจะเป็นตัวเลือกที่ดีกว่าเช่น $users = User::query()->distinct()->get();

หากคุณใช้queryคุณสามารถเลือกคอลัมน์ทั้งหมดตามที่ร้องขอ


6

**

ผ่านการทดสอบสำหรับ Laravel 5.8

**

เนื่องจากคุณต้องการรับคอลัมน์ทั้งหมดจากตารางคุณสามารถรวบรวมข้อมูลทั้งหมดแล้วกรองโดยใช้ฟังก์ชัน Collections ที่เรียกว่าUnique

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

หรือ

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

สำหรับข้อมูลเพิ่มเติมคุณสามารถตรวจสอบLaravel Collection Documentations

แก้ไข: คุณอาจมีปัญหาเกี่ยวกับประสิทธิภาพโดยใช้ Unique () คุณจะได้รับข้อมูลทั้งหมดก่อนจากตารางผู้ใช้จากนั้น Laravel จะกรองข้อมูลนั้น วิธีนี้ไม่ดีหากคุณมีข้อมูลผู้ใช้จำนวนมาก คุณสามารถใช้ตัวสร้างแบบสอบถามและเรียกแต่ละฟิลด์ที่คุณต้องการใช้เช่น:

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

สิ่งนี้ไม่ได้ผลเนื่องจากคุณได้รับข้อมูลทั้งหมดก่อนจาก db
Razor Mureithi

นั่นเป็นเหตุผลที่เรียกว่าการกรองคุณอาจใช้แตกต่าง () สำหรับตัวสร้างแบบสอบถาม แต่คุณไม่สามารถรับฟิลด์อื่นได้ (อย่างไรก็ตามคุณยังสามารถเรียกแต่ละฟิลด์ผ่านการเลือก) การใช้ unique () เป็นวิธีเดียวในการรับฟิลด์ทั้งหมดโดยไม่ต้องเรียกแต่ละฟิลด์ (แม้ว่าเราจะรู้ว่าอาจมีปัญหากับประสิทธิภาพ)
Robby Alvian Jaya Mulia

5

$users = User::select('column1', 'column2', 'column3')->distinct()->get();ดึงข้อมูล coulmns ทั้งสามสำหรับแถวที่แตกต่างกันในตาราง คุณสามารถเพิ่มคอลัมน์ได้มากเท่าที่คุณต้องการ


3

ฉันพบว่าวิธีนี้ใช้ได้ผลดี (สำหรับฉัน) ในการสร้างอาร์เรย์ของค่าที่ไม่ซ้ำกัน:

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

หากคุณ->toSql()ใช้ตัวสร้างแบบสอบถามนี้คุณจะเห็นว่าสร้างแบบสอบถามดังนี้:

select distinct `name` from `users`

->pluck()จะถูกจัดการโดยส่องสว่าง \ 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

A. บันทึกใน 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. ผลการจัดกลุ่มที่ต้องการ

UserFile :: select ('type', 'url', 'updated_at) -> แตกต่าง (' 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()"คุณจะได้รับระเบียนเหมือนกับ A. เนื่องจากแต่ละแถวใน DB ต่างกัน



โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.