Laravel:Eloquentで2つのテーブルの不一致を抽出する
Laravel:Eloquentで2つのテーブルの不一致を抽出する
私はもともとフレームワークを使わずPHPを書いていたので作成した自作システムを作るまでDBの抽出はSQLを直接書いていました。その為、自作システム作成初期に書いたコードは(Eloquentでの文が分からず)DB::と書き出したものがいくつかあります。でもこれ、Laravelの中だと処理が多少遅いようで現在Eloquentに書換をしています。
そんな中「不一致Queryが超楽じゃん!」との感想と「???」との感想と共にあった為、備忘録としてここに記載します。
超簡単な不一致QueryのEloquentコード【doesntHave】
ノーマルなSQLではこんな感じでしょう。
select * From tableA Left Join tableB On (tableA.connect_id = tableB.id) Where tableB.id IS NULL
やっている事は次の通りです。
- tableAにtableBを見える形(Left Join)で結合する
- JoinしたのにtableBが関連づいていない(NULLである)tableAを抽出する
- 結果、tableBに存在しないtableAのレコードが取り出される
これが、なんとEloquentだと一発!
# 'tableB'にはテーブル名ではなくtableB のModel名を記載
$query = tableA::doesntHave('tableB')->get();
これは超楽。破壊力半端ない。
もうちょっと複雑に取得するなら【whereDoesntHave】
JOIN先のテーブルで抽出条件を付けいたい時があります。その時は【whereDosentHave】が重宝します。
#tableB.number = 1、tableB.shop_id = NULL の時の値との不一致を抽出
$query1 = tableA::whereDoesntHave('tableB', function ($a){
$a->where('number',1)
->whereNull('shop_id');
})->get();
お~~、これはSQL書くよりだいぶ楽です。
但し、予定と違う値が抽出されてしまうことがあり、少し悩みました。
抽出条件 whereNotNullがもう一つ絡むと何故か正しい値が取得できない。
$query1 = tableA::whereDoesntHave('tableB', function ($a){
$a->where('number',1)
->whereNull('shop_id')
->whereNotNull('item_id');
})->get();
何をしたかったかと言うと次の通りです。
- 商品ID(tableB.item_id)が存在する
- 店舗ID(tableB.shop_id)が空白
- tableB.number=1
- 1~3の条件を満たすレコードが無いtableAのリストを作成
上のEloquentでは何故か1~3をともに満たす条件ではなく【商品ID(tableB.item_id)が存在する】商品が抽出されてしまいました。これは次のように分けて書く事で改善しました。
$query = tableA::orderBy('id','asc');
$query->whereDoesntHave('tableB', function ($a){
$a->where('number',1)
->whereNull('shop_id');
});
$query->whereHas('tableB', function ($b){
$b->whereNotNull('item_id');
});
$query1 = $query->get();
whereNotNULLが orWhereNotNULLの様に扱われた点は謎です。が解決策を見つけたので良しとしましょう。
何よりもDB::よりも明らかに処理が早い!40万レコードあっても待つのは一瞬です。
まとめ
いやぁ~、Eloquentってすごい!


