Laravel Query Builder 複雜查詢案例:子查詢實現分區查詢 partition by

来源:https://www.cnblogs.com/iBrand2018/archive/2018/03/28/8664511.html
-Advertisement-
Play Games

案例 案例:Laravel 在文章列表中附帶上前10條評論?,在獲取文章列表時同時把每個文章的前10條評論一同查詢出來。 這是典型分區查詢案例,需要根據 comments 表中的 post_id 欄位進行分區,同時根據條件進行排序,把符合條件的前 N 條是數據取出來。 在其他資料庫(Oracle,  ...


案例

案例:Laravel 在文章列表中附帶上前10條評論?,在獲取文章列表時同時把每個文章的前10條評論一同查詢出來。

這是典型分區查詢案例,需要根據 comments 表中的 post_id 欄位進行分區,同時根據條件進行排序,把符合條件的前 N 條是數據取出來。

在其他資料庫(OracleSQL ServerVertica) 包含了 row_number partition by 這樣的函數,能夠比較容易的實現。

比如在 SQL Server 中:

SELECT * FROM (
SELECT *, row_number() OVER (partition by post_id ORDER BY created_at desc) rank FROM comments where post_id in (1,2,3,4,5) 
) b where rand < 11;

在 mysql 中要複雜一些,我們先來看看上面案例中實現需求的幾種解決辦法。

解決辦法

方法1:

在 blade 中要顯示評論數據的地方 post->comments()->limit(10)

問題:如果取了 20 條 Post 數據,就會有 20 條取 comments 的 sql 語句,會造成執行的 sql 語句過多。

不是非常可取,主要問題會造成 SQL 語句過多,對資料庫伺服器產生壓力,不過這裡可以使用緩存來改進,但是不在本文章討論範圍里。

方法2:

直接通過 with 把 Post 的所有 comments 數據都取出來,在 blade 中 post->comments->take(10)

問題:Laravel 會預先把文章所有的評論數據查詢出來,如果文章的評論數據非常多,可能會造成記憶體泄漏。

方法3:

$posts = Post::paginate(15);

$postIds = $posts->pluck('id')->all();

//找出符合條件的 comments ,同時定義 @post, @rank 變數,這裡沒有用 all,get 等函數,此時並不會執行 SQL 語句。
$sub = Comment::whereIn('post_id',$postIds)->select(DB::raw('*,@post := NULL ,@rank := 0'))->orderBy('post_id');

//把上面構造的 sql 查詢作為子表進行查詢,根據 post_id 進行分區的同時 @rank 變數不斷+1
$sub2 = DB::table( DB::raw("({$sub->toSql()}) as b") )
            ->mergeBindings($sub->getQuery())
            ->select(DB::raw('b.*,IF (
            @post = b.post_id ,@rank :=@rank + 1 ,@rank := 1
        ) AS rank,
        @post := b.post_id'));

//取出符合條件的前10條comment
$commentIds = DB::table( DB::raw("({$sub2->toSql()}) as c") )
            ->mergeBindings($sub2)
        ->where('rank','<',11)->select('c.id')->pluck('id')->toArray();

$comments = Comment::whereIn('id',$commentIds)->get();

$posts = $posts->each(function ($item, $key) use ($comments) {
    $item->comments = $comments->where('post_id',$item->id);
});
會產生三條sql
select * from `posts` limit 15 offset 0;

select `c`.`id` from (select b.*,IF (
@post = b.post_id ,@rank :=@rank + 1 ,@rank := 1
) AS rank,
@post := b.post_id from (select *,@post := NULL ,@rank := 0 from `comments` where `post_id` in ('2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16') order by `post_id` asc) as b) as c where `rank` < '11';

select * from `comments` where `id` in ('180', '589', '590', '3736');

知識點

  1. toSql() 方法的作用是為了獲取不帶有 binding 參數的 SQL, 也就是說帶問號的 SQL
  2. getQuery() 方法的作用是為了獲取 binding 參數並代替 toSql() 獲得SQL的問號,從而得到完整的SQL
  3. raw() 的作用是直接把 SQL 套進 Laravel 的查詢構造器中。
  4. mysql 查詢語句中定義變數 @post := NULL ,@rank := 0 以及 IF 函數的使用
  5. 如何構建子查詢。

為什麼不直接用原生 SQL 語句來實現?

這裡之所以堅持使用 Laravel Query Builder 來實現,可以有效防止 SQL 註入,並且和 ORM 的 Model 對象關聯起來。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先: vim /etc/yum/pluginconf.d/langpacks.conf將第一行:enable=1改為enable=0 然後執行一下yum命令,發現還會占用,殺死線程即可。 ...
  • 先對網路介面配置文件ifcfg-eth0進行設置1.cd /etc/sysconfig/network-scripts/2.vi ifcfg-eth0# Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE]DEVICE=eth0 ##設備名稱BOOT... ...
  • 前言: 本文是對這篇博客Maximum Size Of A Logical Volume In LVM的翻譯,敬請尊重原創和翻譯勞動成果,那些隨意轉載的大爺們,好歹也自覺註明出處。謝謝! 英文原文地址:https://www.walkernews.net/2007/07/02/maximum-siz... ...
  • RAID是“Redundant Array of Independent Disk”的縮寫,中文意思是獨立冗餘磁碟陣列。簡單地解釋,就是將N台硬碟通過RAID Controller(分Hardware,Software)結合成虛擬單臺大容量的硬碟使用。RAID的採用為存儲系統(或者伺服器的內置存儲... ...
  • 先講下事務執行流程: BEGIN和COMMIT 結果: ROLLBACK 結果: 由上可知BEGIN TRAN 開始事務,使事務數量加一 COMMIT TRAN 使事務減一,提交最新開闢的事務 ROLLBACK 使事務數量直接減為0,也就是回滾 事務分類: 1.顯示事務 2.隱式事務模式 3.自動提 ...
  • 40、子查詢:出現在其他SQL語句里的SELECT語句 例如:SELECT sname,mark FROM student WHERE mark = (SELECT max(mark) FROM student); SELECT sname,mark FROM student WHERE mark ...
  • #求最高工資的員工信息 SELECT * FROM emp WHERE sal = (SELECT max(sal) FROM emp); #刪除工資最低的員工信息 DELETE FROM emp WHERE sal = (SELECT e.s FROM (SELECT min(sal) s FRO ...
  • 17、創建表 CREATE TABLE tbname(columnname1 類型 約束條件, columnname2 類型 約束條件,…); 三大類型:數值型,時間日期型,字元串類型 六大約束條件: PRIMARY KEY(主鍵) NOT NULL(非空) AUTO_INCREMENT(自增長) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...