Làm cách nào để nhận các giá trị khác biệt cho các trường cột không phải khóa trong Laravel?


94

Điều này có thể khá dễ dàng nhưng không biết làm thế nào.

Tôi có một bảng có thể có các giá trị lặp lại cho một trường cột không phải khóa cụ thể. Làm cách nào để viết một truy vấn SQL bằng cách sử dụng Trình tạo truy vấn hoặc Eloquent sẽ tìm nạp các hàng có giá trị riêng biệt cho cột đó?

Lưu ý rằng tôi không chỉ tìm nạp cột đó, nó kết hợp với các giá trị cột khác, vì vậy distinct()có thể không thực sự hoạt động. Vì vậy, câu hỏi đó về cơ bản có thể là làm thế nào để chỉ định cột mà tôi muốn khác biệt trong một truy vấn bây giờ distinct()không chấp nhận tham số?

Câu trả lời:


115

Bạn nên sử dụng groupby. Trong Trình tạo truy vấn, bạn có thể thực hiện theo cách này:

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

2
Tôi có một bảng "tin nhắn" (được sử dụng để trò chuyện) và tôi muốn nhận tin nhắn mới nhất mà người dùng đã xác thực đã nhận được từ mỗi cuộc trò chuyện. Sử dụng nhóm Bằng cách tôi có thể chỉ nhận được một tin nhắn, nhưng tôi nhận được tin nhắn đầu tiên và tôi cần tin nhắn cuối cùng. Có vẻ như orderBy không hoạt động.
JCarlosR

10
nếu bạn không muốn áp dụng bất kỳ phép toán nào như SUM, AVG, MAX, v.v. "nhóm theo" không phải là câu trả lời chính xác (bạn có thể sử dụng nó, nhưng bạn không nên sử dụng nó). Bạn nên sử dụng riêng biệt. Trong MySQL, bạn có thể sử dụng DISTINCT cho một cột và thêm các cột khác vào tập kết quả: "CHỌN DISTINCT tên, id, email TỪ người dùng". Với tài hùng biện, bạn có thể làm điều này với $ this-> select (['name', 'id', 'email']) -> diff () -> get ();
Sergi

1
khi tôi thêm một where()nó bị hỏng, làm thế nào tôi có thể sửa lỗi này?
tq

1
Khi tôi sử dụng nó, nó hiển thị 'id' và 'email' không có trong groupBy () tôi cần sử dụng groupBy ('id', 'name', 'email'). Cái nào không hữu ích.
Haque

1
Điều cần lưu ý: group bykhông giống như distinct. group bysẽ thay đổi hành vi của các hàm tổng hợp như count()bên trong truy vấn SQL. distinctsẽ không thay đổi hoạt động của các hàm như vậy, vì vậy bạn sẽ nhận được các kết quả khác nhau tùy thuộc vào việc bạn sử dụng hàm nào.
Skeets

76

Trong Eloquent bạn cũng có thể truy vấn như sau:

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


11
Câu hỏi đặc biệt yêu cầuNote that I am not fetching that column only, it is in conjunction with other column values
Hirnhamster

11
@Hirnhamster: Okkei. Tuy nhiên, câu trả lời này vẫn còn hữu ích cho những người khác đi ngang qua ...
Pha-trốt

3
Không hữu ích cho tôi, tôi đang tìm kiếm cụ thể OP là gì. Nó không phải là một dễ dàng tìm thấy.
blamb

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

Ngoài ra bạn cần ->orderBy('name')nếu bạn muốn sử dụng cái này với chunk.
Chibueze Opata

26

một cách hùng hồn, bạn có thể sử dụng cái này

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

groupBy thực sự đang tìm nạp các giá trị riêng biệt, trên thực tế groupBy sẽ phân loại các giá trị giống nhau để chúng ta có thể sử dụng các hàm tổng hợp trên chúng. nhưng trong trường hợp này, chúng tôi không có hàm tổng hợp, chúng tôi chỉ chọn giá trị sẽ khiến kết quả có các giá trị riêng biệt


4
Cái này hoạt động ra sao? GroupBy có thực sự tìm nạp các tên người dùng riêng biệt không? Bạn có thể vui lòng giải thích thêm một chút cách này trả lời vấn đề của op không?
Félix Gagnon-Grenier

Có groupBy thực sự đang tìm nạp các giá trị riêng biệt, trên thực tế groupBy sẽ phân loại các giá trị giống nhau, để chúng ta có thể sử dụng các hàm tổng hợp trên chúng. nhưng trong trường hợp này, chúng tôi không có hàm tổng hợp, chúng tôi chỉ chọn giá trị sẽ khiến kết quả có các giá trị khác biệt.
Salar

Tôi hiểu rồi .. vậy câu trả lời này có khác gì câu trả lời của Marcin không? Có một câu trả lời khác là ok, miễn là bạn có thể giải thích nó khác biệt như thế nào và nó giải quyết được lỗi gì :) Đừng bận tâm trả lời trong phần bình luận, vui lòng chỉnh sửa trực tiếp câu hỏi của bạn với thông tin liên quan.
Félix Gagnon-Grenier

1
kết quả là như nhau, nó phụ thuộc vào dự án của bạn để sử dụng ORM hay sử dụng trình tạo truy vấn, nếu bạn đang sử dụng một trong số chúng, thì tốt hơn hết bạn nên gắn bó với cái đó. đó là lý do tại sao tôi trả lời câu hỏi của bạn theo một cách khác.
Salar

Chà, tôi gặp sự cố này trong đó, nếu bây giờ bạn muốn kiểm tra xem một mục có tồn tại hay không bằng cách sử dụng in_array()hàm, nó không bao giờ hoạt động. Để khắc phục, tôi đã thử ->lists()thay thế (Phiên bản 5.2). Vì vậy, $users = User::select('name')->groupBy('name')->lists('name');hoạt động tốt cho php's in_array();
Pathros

19

Mặc dù tôi đã muộn để trả lời điều này, nhưng một cách tiếp cận tốt hơn để có được các bản ghi khác biệt bằng cách sử dụng Eloquent sẽ là

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

Được rồi, bạn đã thêm giá trị bằng cách cho chúng tôi thấy rằng người ta cũng có thể chuyển các tham số của tên trường cho get()phương thức (và tôi cũng đã thử nghiệm first()phương thức này), tương đương với việc sử dụng select()phương thức như được hiển thị trong một vài câu trả lời ở đây. Mặc dù bằng cách nào đó groupByvẫn có vẻ đạt điểm cao nhất. Tuy nhiên, điều này sẽ thực sự khác biệt nếu đó là cột duy nhất tôi đang chọn.
gthuo

Hiệu suất khôn ngoan, khác biệt sẽ ghi bàn trên groupby, bởi vì các hồ sơ sẽ được lựa chọn lúc đầu trong Engine SQL không giống như trong Nhóm By engine Lựa chọn đầu tiên tất cả hồ sơ và sau đó nhóm là bởi ..
rsakhale

1
$user_names = User::distinct()->count(['name']);cũng hoạt động
Bira

11

Nhóm theo sẽ không hoạt động nếu các quy tắc cơ sở dữ liệu không cho phép bất kỳ trường nào được chọn nằm ngoài một hàm tổng hợp. Thay vào đó hãy sử dụng các bộ sưu tập laravel .

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

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

Ai đó đã chỉ ra rằng điều này có thể không tốt về hiệu suất đối với các bộ sưu tập lớn. Tôi khuyên bạn nên thêm một khóa vào bộ sưu tập. Phương thức sử dụng được gọi là keyBy . Đây là phương pháp đơn giản.

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

KeyBy cũng cho phép bạn thêm chức năng gọi lại cho những việc phức tạp hơn ...

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

Nếu bạn phải lặp lại các bộ sưu tập lớn, việc thêm một khóa vào nó sẽ giải quyết được vấn đề về hiệu suất.


Bạn nói đúng và đó chính xác là vấn đề tôi đang chống lại ... tuy nhiên việc chờ đợi cho đến khi truy vấn được thực thi và thực hiện hành động trong bộ sưu tập cũng không phải là lý tưởng. Đặc biệt nếu bạn dựa vào phân trang, hoạt động của bạn sẽ diễn ra sau khi tính toán phân trang và các mục trên mỗi trang sẽ bị sai. Ngoài ra, DB phù hợp hơn để hoạt động trên các khối dữ liệu lớn hơn là truy xuất và xử lý nó trong mã. Đối với khối dữ liệu nhỏ nó sẽ không còn là một vấn đề
ChronoFish

Bạn có một điểm tốt. Phương pháp này có thể không có hiệu suất tốt trên các bộ sưu tập lớn và điều này sẽ không hoạt động cho việc phân trang. Tôi nghĩ rằng có một câu trả lời tốt hơn những gì tôi có.
Jed Lynch

6

Lưu ý rằng groupBynhư được sử dụng ở trên sẽ không hoạt động cho postgres.

Sử dụng distinctcó lẽ là một lựa chọn tốt hơn - ví dụ: $users = User::query()->distinct()->get();

Nếu bạn sử dụng, querybạn có thể chọn tất cả các cột theo yêu cầu.


6

**

Đã thử nghiệm cho Laravel 5.8

**

Vì bạn muốn lấy tất cả các cột từ bảng, bạn có thể thu thập tất cả dữ liệu và sau đó lọc nó bằng cách sử dụng chức năng Bộ sưu tập có tên Unique

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

hoặc là

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

Để biết thêm thông tin, bạn có thể kiểm tra Tài liệu Bộ sưu tập Laravel

CHỈNH SỬA: Bạn có thể gặp vấn đề với hiệu suất, bằng cách sử dụng Unique (), trước tiên bạn sẽ nhận được tất cả dữ liệu từ bảng Người dùng, và sau đó Laravel sẽ lọc nó. Cách này không tốt nếu bạn có nhiều dữ liệu Người dùng. Bạn có thể sử dụng trình tạo truy vấn và gọi từng trường mà bạn muốn sử dụng, ví dụ:

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

Điều này không hiệu quả vì bạn lấy tất cả dữ liệu trước tiên từ db
Razor Mureithi

Đó là lý do tại sao Nó được gọi là lọc, bạn có thể sử dụng Other () cho trình tạo truy vấn nhưng bạn không thể lấy các trường khác (dù sao thì bạn vẫn có thể gọi từng trường thông qua select). Bằng cách sử dụng unique () là cách duy nhất để lấy tất cả các trường mà không cần gọi từng trường (Mặc dù chúng tôi biết rằng điều đó có thể có vấn đề với hiệu suất).
Robby Alvian Jaya Mulia

5

$users = User::select('column1', 'column2', 'column3')->distinct()->get();truy xuất tất cả ba coulm cho các hàng riêng biệt trong bảng. Bạn có thể thêm bao nhiêu cột tùy thích.


3

Tôi thấy phương pháp này hoạt động khá tốt (đối với tôi) để tạo ra một mảng phẳng các giá trị duy nhất:

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

Nếu bạn đã chạy ->toSql()trên trình tạo truy vấn này, bạn sẽ thấy nó tạo ra một truy vấn như sau:

select distinct `name` from `users`

Việc ->pluck()này được xử lý bởi lib Illusion \ collection (không thông qua truy vấn sql).


1

Tôi đã gặp vấn đề tương tự khi cố gắng điền vào danh sách tất cả các chuỗi duy nhất mà người dùng có với những người dùng khác. Điều này đã làm nên mẹo cho tôi

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
$users = Users::all()->unique('name');

5
Vui lòng giải thích CÁCH đoạn mã của bạn giải quyết vấn đề của OP.
ifconfig

0

Đối với những người như tôi đang làm cùng một sai lầm. Đây là câu trả lời công phu được kiểm tra trong Laravel 5.7

A. Bản ghi trong 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. Kết quả được nhóm mong muốn

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

Vì vậy, chỉ chuyển những cột đó vào "select()", giá trị của chúng giống nhau. Ví dụ: 'type','url'. Bạn có thể thêm nhiều cột hơn với điều kiện chúng có cùng giá trị 'updated_at'.

Nếu bạn cố gắng vượt qua "created_at"hoặc "id"vào "select()", thì bạn sẽ nhận được các bản ghi giống như A. Vì chúng khác nhau đối với mỗi hàng trong DB.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.