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

やっている事は次の通りです。

  1. tableAにtableBを見える形(Left Join)で結合する
  2. JoinしたのにtableBが関連づいていない(NULLである)tableAを抽出する
  3. 結果、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();

何をしたかったかと言うと次の通りです。

  1. 商品ID(tableB.item_id)が存在する
  2. 店舗ID(tableB.shop_id)が空白
  3. tableB.number=1
  4. 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ってすごい!

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください