Laravel關聯查詢限制條數和分組查詢顯示為零的結果


1. 需求

目前有這樣兩個需求:

1) 查詢用戶,顯示用戶的信息以及他寫過的書籍。如果用戶有書籍,則顯示,最多顯示2本。如果沒有,則不顯示書籍。

2) 顯示用戶的id號以及對應的書籍件數(只用SQL實現,不使用業務邏輯)。

2. 准備

        本文中用到的user模型,數據,控制器,路由之類的都已經在另一篇文章 手摸手教你讓Laravel開發Api更得心應手 創建好了。

users表中的數據

users-data

books表中的數據

books-data

3. 關聯查詢限制條數

這個比較容易,只要在關聯函數限制條數即可。

3.1. 創建Book模型

1
php artisan make:model Models/Book

3.2. 添加關聯函數並且限制條數

編輯 app/Models/User.php,添加關聯函數

1
2
3
public function books(){
return $this->hasMany(Book::class,'user_id','id')->limit(2); //一對多,最多關聯2條
}

 

3.3. 測試

app/Http/Controllers/Api/UserController.php里,隨意添加一個測試函數

1
2
3
4
5
//關聯查詢限制條數
public function test(Request $request){
$users= User::with('books')->get();
return $users;
}

 

測試結果,符合要求,id為1的用戶原來是3本書籍,現在只被取出2本。

with-limit

4. 分組查詢顯示為零的結果

4.1. SQL語句

一開始,我們會這樣寫SQL語句

1
select `u`.`id`,`u`.`name`,`u`.`num` from `users` as `u` left join (select `user_id`,count(*) as `num` from books group by `user_id`) as `b` on `u`.id = `b`.user_id

最后顯示如下,並不會將沒有的顯示為0

books-sql-origin-result

所以我們稍加修改,用上MySQL的內置函數

1
select distinct `u`.`id`,`u`.`name`,IFNULL( `b`.`num`, 0 ) AS num from `users` as `u` left join (select `user_id`,count(*) as `num` from books group by `user_id`) as `b` on `u`.id = `b`.user_id

符合我們的需求。

books-sql-result

4.2. Laravel框架中使用

寫SQL很容易,那我們應該如何在框架中使用呢(不允許查完再用業務邏輯后獲得答案)?同時我們再附加一個條件,只要id為1,2,3,4,5的用戶。

4.2.1. 直接編寫

查詢Laravel手冊,參考查詢構造器高級join語句,我們會立刻想到下面這樣編寫

1
2
3
4
5
6
7
8
9
10
11
12
13
public function test3(){
//統計出所有內部員工的user_id
$user_ids = [1,2,3,4,5];
$users = User::selectRaw('u.id,IFNULL( b.number, 0 ) AS number')
->from('users as u')
->distinct()
->whereIn('id', $user_ids)
->leftJoin('books as b',function ($join) use($user_ids){
$join->selectRaw('user_id,count(*) as number')->whereIn('user_id', $user_ids)->groupBy('user_id')->on('u.id', '=', 'b.user_id');
})
->get();
return $users;
}

 

測試的時候我們發現報了錯

1
Unknown column 'b.number' in 'field list' (SQL: select distinct u.id,IFNULL( b.number, 0 ) AS number from `users` as `u` left join `books` as `b` on `user_id` in (1, 2, 3, 4, 5) and `u`.`id` = `b`.`user_id` where `id` in (1, 2, 3, 4, 5))

 

最后的SQL語句跟我們想象中的不太一樣。

4.2.2. 問題分析

錯誤的原因是,我們其實是使用left join連接了子查詢,但是Laravel的聯表查詢,例如joinlefeJoinrightJoin等,經過個人的測試,這些閉包並不能實現子查詢的。所以最后獲得的SQL語句是錯誤的。

Laravel官方文檔的子查詢並沒有這方面詳細的介紹,所以我們一起來了解一下其他地方查來的資料

4.2.3. Query Builder

4.2.3.1. TOSQL()

toSql()方法的作用是為了獲取不帶有binding參數的SQL

例如:

1
select * from `users` where `users`.`id` = ?

4.2.3.2. GETQUERY()

getQuery()方法的作用是為了獲取binding參數並代替toSql()獲得SQL的問號,從而得到完整的SQL
例如:

1
select * from `users` where `users`.`id` = 1

4.2.4. 修復問題

現在我們使用Query Builder來修復一下之前的問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public function test2(){

//統計出所有內部員工的user_id

$user_ids = [1,2,3,4,5];

$bookQuery = Book::selectRaw('user_id,count(*) as number')->whereIn('user_id', $user_ids)->groupBy('user_id'); //制作一個query builder

$users = User::selectRaw('u.id,IFNULL( b.number, 0 ) AS number')
->from('users as u')
->distinct()
->whereIn('id', $user_ids)
->leftJoin(\DB::raw("({$bookQuery->toSql()}) as b"),function ($join) use($bookQuery){
//toSql()返回的是等待綁定參數的SQL語句
$join->mergeBindings($bookQuery->getQuery())->on('u.id','=','b.user_id');
//mergeBindings是將SQl的參數進行綁定
})
->get();

return $users;
}

4.2.5. 測試

最后的結果符合我們的需求
books-query-builder

本文作者: guaosi 
本文鏈接:  https://www.guaosi.com/2019/03/19/laravel-with-limit-and-group-show-zero/ 
版權聲明: 本博客所有文章除特別聲明外,均采用  CC BY-NC-SA 3.0 許可協議  知識共享許可協議 進行許可。轉載請注明出處!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM