Eloquent 的 whereHas 採用 where in 實現的優化

biiiiiigmonster發表於2020-12-22

hasin是一個基於where in語法實現的Laravel ORM關聯關係查詢的擴充套件包,部分業務場景下可以替代Laravel ORM中基於where exists語法實現的has,以獲取更高的效能。

Github (喜歡這個專案點個 star 吧,灰常感謝~)

簡介

Laravel ORM的關聯關係非常強大,基於關聯關係的查詢has也給我們提供了諸多靈活的呼叫方式,然而某些情形下,has使用了where exists語法實現

select * from A where exists (select * from B where A.id=B.a_id)

exists是對外表做loop迴圈,每次loop迴圈再對內表(子查詢)進行查詢,那麼因為對內表的查詢使用的索引(內表效率高,故可用大表),而外表有多大都需要遍歷,不可避免(儘量用小表),故內表大的使用exists,可加快效率。

但是當A表資料量較大的時候,就會出現效能問題,那麼這時候用where in語法將會極大的提高效能

select * from A where A.id in (select B.a_id from B)

in是把外表和內表做hash連線,先查詢內表,再把內表結果與外表匹配,對外表使用索引(外表效率高,可用大表),而內表多大都需要查詢,不可避免,故外表大的使用in,可加快效率。

因此在程式碼中使用has(hasMorph)或者hasIn(hasMorphIn)應由資料體量來決定……

<?php
/**
 * SQL:
 * 
 * select * from `product` 
 * where exists 
 *   ( 
 *      select * from `product_skus` 
 *      where `product`.`id` = `product_skus`.`p_id` 
 *      and `product_skus`.`deleted_at` is null 
 *   ) 
 * and `product`.`deleted_at` is null 
 * limit 10 offset 0
 */
$products = Product::has('skus')->paginate(10);

/**
 * SQL:
 * 
 * select * from `product` 
 * where `product`.`id` IN  
 *   ( 
 *      select `product_skus`.`p_id` from `product_skus` 
 *      and `product_skus`.`deleted_at` is null 
 *   ) 
 * and `product`.`deleted_at` is null 
 * limit 10 offset 0
 */
$products = Product::hasIn('skus')->paginate(10);

Laravel ORM十種關聯關係案例sql輸出可檢視有道雲筆記

環境

  • PHP >= 7
  • laravel >= 5.5

安裝

composer require biiiiiigmonster/hasin

使用

在配置檔案app.php新增配置,自動註冊服務

<?php
    // ...

    'providers' => [
        // ...

        BiiiiiigMonster\Hasin\HasinServiceProvider::class,// hasin擴充套件包引入
    ],

此擴充套件hasIn(hasMorphIn)支援Laravel ORM中的所有關聯關係,入參及呼叫方式與has(hasMorph)完全一致,可安全使用或替換

hasIn

// hasIn
Product::hasIn('skus')->get();

// orHasIn
Product::where('name', 'like', '%拌飯醬%')->orHasIn('skus')->get();

// doesntHaveIn
Product::doesntHaveIn('skus')->get();

// orDoesntHaveIn
Product::where('name', 'like', '%拌飯醬%')->orDoesntHaveIn('skus')->get();

whereHasIn

// whereHasIn
Product::whereHasIn('skus', function ($query) {
    $query->where('sales', '>', 10);
})->get();

// orWhereHasIn
Product::where('name', 'like', '%拌飯醬%')->orWhereHasIn('skus', function ($query) {
    $query->where('sales', '>', 10);
})->get();

// whereDoesntHaveIn
Product::whereDoesntHaveIn('skus', function ($query) {
    $query->where('sales', '>', 10);
})->get();

// orWhereDoesntHaveIn
Product::where('name', 'like', '%拌飯醬%')->orWhereDoesntHaveIn('skus', function ($query) {
    $query->where('sales', '>', 10);
})->get();

hasMorphIn

Image::hasMorphIn('imageable', [Product::class, Brand::class])->get();

巢狀關聯

Product::hasIn('attrs.values')->get();

自關聯

Category::hasIn('children')->get();

鳴謝

給 Eloquent 的 whereHas 加個 where in 的優化
[擴充套件包] Laravel-wherehasin 提升 ORM 關聯關係查詢效能 (優化 whereHas 效能)
感謝這兩篇博文網友的討論以及提供的思路

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章