一、引言

在使用 Laravel 進行開發的過程中,打印 SQL 語句是一項非常重要且實用的操作。它有著諸多應用場景,比如當我們使用 Query Builder 構建查詢時,只有清楚地知道到底執行了什么 SQL 語句,才能正確且高效地編寫 Query Builder。像 “熱加載” 和 “懶加載” 場景中,了解 SQL 的執行情況,就能避免一些效率問題的產生。例如在熱加載場景下,像 $user = User::where('name', 'Eric')->with('articles')->first(); 這樣的語句,Query Builder 可能只使用類似 select * from users where name = 'Eric' limit 1; 以及 select * from articles where user_id in(22); 這樣兩條 SQL 語句,后續遍歷用戶文章時就無需再頻繁請求數據庫了。而與之相對的懶加載,如果不清楚底層 SQL 執行情況,就可能出現明明可以少量次數完成的查詢,卻執行了多次 SQL 查詢的 “N + 1” 問題,導致效率變低。另外,在處理一些較為復雜的業務場景,例如拼接多個 whereRaw 的 SQL 語句時,若程序執行結果與預期不符,通過打印 SQL 語句,觀察其實際執行的內容,就能排查出是語句本身構造問題,還是框架執行層面出現了差錯。而且在調試自定義的參數綁定查詢時,比如根據經緯度手動計算兩點之間的近距離這類涉及到自定義 SQL 語句和參數綁定的情況,將 SQL 語句打印出來,有助于確認參數是否正確傳入、語句是否符合預期等,方便我們對接口執行的查詢語句做具體分析,從而更好地完成開發與調試工作,確保項目順利進行??傊?,掌握 Laravel 中打印 SQL 語句的方法,能為我們開發帶來極大的便利呢。
二、Laravel 打印 SQL 語句的方法
(一)開啟執行日志法
在 Laravel 中,一種常見的打印 SQL 語句的方法是開啟執行日志。具體操作是使用 DB::connection()->enableQueryLog(); 來開啟日志記錄,然后執行我們的數據庫查詢操作,比如 $result = DB::table('advert')->whereJsonContains('tag', "1")->get();,最后通過 dd(DB::getQueryLog()); 就能獲取到執行的查詢語句以及相關的綁定參數和執行時間等信息。這種方法的優點在于它能完整地記錄下所有執行過的 SQL 語句,對于我們排查一些復雜業務邏輯中 SQL 的執行情況非常有幫助。例如在一個涉及多個關聯表查詢和條件篩選的功能模塊中,如果出現數據不準確的問題,通過開啟執行日志,我們可以清晰地看到每一步查詢的具體情況,從而快速定位是哪一個查詢出現了偏差。然而,它也有一定的局限性,開啟執行日志會在一定程度上影響程序的性能,尤其是在生產環境中,如果大量使用這種方式來打印 SQL 語句,可能會導致系統響應變慢。而且日志信息是存放在內存中的,如果查詢語句較多或者數據量較大,可能會占用較多的內存資源。所以這種方法比較適合在開發環境或者測試環境中,對一些關鍵的業務邏輯進行 SQL 語句的排查和調試。
(二)toSql () 方法
另一種打印 SQL 語句的方法是使用 toSql() 方法。比如對于 $query = DB::table('users')->where('id', 10);,我們可以通過 $query->toSql(); 來獲取對應的 SQL 語句。這種方法的優勢在于它的使用非常簡便,能夠快速地獲取到我們構建的查詢語句的大致樣子。不過它也存在一些不足,toSql() 方法獲取到的 SQL 語句中參數是用 ? 占位符表示的,并沒有顯示出實際的參數值。如果我們想要獲取完整的帶有實際參數值的 SQL 語句,還需要進一步處理,例如 $sql = str_replace_array('?', $query->getBindings(), $query->toSql()); 這樣的操作來替換占位符,相對來說比較麻煩。而且這種方法只能獲取到當前構建的這一條 SQL 語句,如果在一個方法或者一個業務流程中存在多條 SQL 語句的執行,使用 toSql() 方法就需要逐個去獲取,不如開啟執行日志法那樣可以一次性獲取到所有執行過的 SQL 語句。但在一些簡單的場景下,比如我們只是想快速查看一下某一條特定查詢語句的基本結構,toSql() 方法還是比較方便快捷的。
(三)推薦方法:在 AppServiceProvider 中處理
這里推薦一種更為實用和穩定的方法,即在 AppServiceProvider 的 boot 方法中進行處理。首先,我們打開 app/Providers/AppServiceProvider.php 文件,在 boot 方法中添加以下代碼:通過這種方式,我們可以將所有執行的 SQL 語句以及完整的參數信息記錄到指定的日志文件中,比如 storage/logs/2024-12-25_query.log(日期會根據實際執行日期生成)。這樣做的好處是,在開發和調試過程中,我們可以隨時查看這些日志文件,了解程序在運行過程中到底執行了哪些 SQL 語句,并且能夠清晰地看到完整的 SQL 語句內容,包括參數值。而且這種方法對程序性能的影響相對較小,因為它是在一個統一的地方進行日志記錄的處理,不會像開啟執行日志法那樣在每個查詢的地方都進行額外的日志記錄操作,從而避免了過多的性能開銷。同時,將日志記錄到文件中也便于我們長期保存和分析,對于后續排查一些線上環境中偶爾出現的數據庫相關問題也提供了有力的支持。所以,在實際的 Laravel 項目開發中,這種在 AppServiceProvider 中處理 SQL 語句打印的方法是一個比較好的選擇。
三、不同方法的對比與選擇
(一)使用便捷性對比
開啟執行日志法:使用時需要先通過 DB::connection()->enableQueryLog(); 開啟日志記錄,執行查詢操作后,再用 dd(DB::getQueryLog()); 來獲取信息,整體步驟不算復雜,但相對而言,在每個需要查看 SQL 語句的地方都要進行這樣的操作,略顯繁瑣。并且在生產環境大量使用還可能影響性能,所以更適合開發和測試環境,使用場景上有一定限制,便捷性打個中等分吧。toSql () 方法:使用起來非常簡便,比如對于構建的查詢語句 $query = DB::table('users')->where('id', 10);,只需通過 $query->toSql(); 就能快速獲取到 SQL 語句的大致樣子,在只想簡單查看某一條特定查詢語句結構時很方便快捷,便捷性方面表現較好,可以打高分。在 AppServiceProvider 中處理:需要打開 app/Providers/AppServiceProvider.php 文件,在 boot 方法中添加相應代碼來處理 SQL 語句的記錄和打印,初次配置時步驟稍多一些。不過一旦配置好,后續在開發和調試過程中,無需過多額外操作就能自動記錄 SQL 語句到日志文件,從長期使用和整體項目角度來看,還是比較方便的,便捷性也能給到中等偏上的分數。
(二)獲取信息完整性對比
開啟執行日志法:它的優勢十分明顯,能夠完整地記錄下所有執行過的 SQL 語句,還能包含相關的綁定參數和執行時間等詳細信息,這對于排查復雜業務邏輯中 SQL 的執行情況幫助極大,信息完整性方面可以打高分。toSql () 方法:其獲取到的 SQL 語句中參數是用 ? 占位符表示的,并不會直接顯示出實際的參數值,如果要獲取完整的帶有實際參數值的 SQL 語句,還需要進行如 $sql = str_replace_array('?', $query->getBindings(), $query->toSql()); 這樣額外的處理操作,所以在信息完整性上有所欠缺,只能打低分。在 AppServiceProvider 中處理:通過這種方式可以將所有執行的 SQL 語句以及完整的參數信息記錄到指定的日志文件中,我們能隨時查看日志文件了解完整的 SQL 語句內容,包括參數值,在信息完整性方面表現優秀,同樣可以打高分。
(三)性能影響對比
開啟執行日志法:開啟執行日志會在一定程度上影響程序的性能,尤其是在生產環境中,如果大量使用這種方式來打印 SQL 語句,可能會導致系統響應變慢。而且日志信息是存放在內存中的,當查詢語句較多或者數據量較大時,還可能占用較多的內存資源,性能影響方面表現較差,只能打低分。toSql () 方法:它只是獲取當前構建的這一條 SQL 語句,相對來說對性能的影響較小,不過如果在一個業務流程中有多條 SQL 語句需要查看,逐個獲取的過程可能也會有一定的性能損耗,但總體在這三種方法里對性能影響算是比較小的,可打中等分。在 AppServiceProvider 中處理:因為是在一個統一的地方進行日志記錄的處理,不會像開啟執行日志法那樣在每個查詢的地方都進行額外的日志記錄操作,從而避免了過多的性能開銷,對程序性能的影響相對較小,在性能影響這塊能打高分。
(四)選擇建議
綜合以上對比,如果您只是想快速查看某一條查詢語句的大致結構,方便簡單驗證,那么 toSql() 方法是不錯的選擇,它操作簡便,能迅速滿足需求。要是處于開發環境或者測試環境,需要排查復雜業務邏輯中 SQL 的具體執行情況,對信息完整性要求很高,并且不太在意一定程度的性能影響以及內存占用問題時,開啟執行日志法可以很好地幫助您定位問題,清晰呈現每一步查詢情況。而對于實際的 Laravel 項目開發,特別是需要長期記錄和查看 SQL 語句,兼顧信息完整性、性能以及方便后續排查線上環境中偶爾出現的數據庫相關問題,建議采用在 AppServiceProvider 中處理的方法,雖然配置時稍麻煩一點,但從整個項目周期來看,是最為實用和穩定的選擇哦。
四、實際應用案例展示
以下我們通過一個具體的復雜查詢案例,來分別使用上述三種方法進行 SQL 語句打印,展示在實際開發中如何運用這些方法進行調試和優化。假設我們正在開發一個電商項目,現在有一個需求是查詢出滿足特定條件的商品列表以及對應的商家信息,同時還要根據商品的銷量進行降序排序,并且只獲取前 10 條記錄。
(一)使用開啟執行日志法
首先,我們使用開啟執行日志法來查看執行的 SQL 語句。在相關的控制器方法或者業務邏輯代碼處,添加以下代碼:通過這樣的操作,我們可以獲取到類似如下的執行日志信息:從這個結果中,我們可以清晰地看到實際執行的 SQL 語句結構以及參數綁定情況。比如這里能明確看到 where 條件中商品狀態的參數值是 1,同時知道是按照 products.sales 字段進行降序排序并且限制了獲取 10 條記錄。在排查數據是否正確顯示、關聯查詢是否符合預期等問題時,這樣完整的日志信息就很有用了。例如,如果發現獲取到的商品列表中包含了狀態不為 1 的商品,那就可以順著這條 SQL 語句去檢查是條件判斷邏輯有誤,還是數據在存入時狀態值就出現了差錯等情況。不過要注意,在生產環境中如果大量使用這種方式,尤其是面對頻繁的查詢操作時,可能會因為內存占用以及對性能的影響,導致系統響應變慢等問題哦,所以它更適合在開發環境或者測試環境中幫助我們排查問題呢。
(二)使用 toSql () 方法
接下來,我們用 toSql() 方法來嘗試獲取 SQL 語句。代碼可以這樣寫:運行后,得到的結果大概是這樣:可以看到,使用 toSql() 方法能快速獲取到查詢語句的基本結構,非常簡便直觀。但正如前面所說,它存在參數顯示不完整的問題,這里的參數都是用 ? 占位符表示的。如果我們想要確切知道參數值,還需要進一步處理,像下面這樣:經過替換操作后,才能完整顯示出帶有實際參數值的 SQL 語句。在這個案例中,如果只是想快速查看一下構建的這個查詢語句的大致樣子,不想去開啟執行日志等相對麻煩一點的操作時,toSql() 方法就可以滿足需求了,能讓我們迅速知曉查詢的基本邏輯和結構是否正確。
(三)使用在 AppServiceProvider 中處理的方法
最后,我們看看在 AppServiceProvider 中處理的方法如何運用在這個案例中。先打開 app/Providers/AppServiceProvider.php 文件,在 boot 方法中添加之前介紹的代碼:然后正常執行查詢操作,也就是和前面類似的代碼:這時,我們可以到對應的日志文件(例如 storage/logs/2024-12-25_query.log,日期根據實際執行時間而定)中去查看記錄的 SQL 語句信息,可能會看到類似這樣的內容:這種方式不僅能完整地記錄下帶有實際參數值的 SQL 語句,而且對程序性能的影響相對較小,還便于我們長期保存和后續分析。比如在后續項目上線后,如果偶爾出現和這個查詢相關的數據異常問題,我們就可以通過查看這些歷史的日志文件,快速定位是不是 SQL 語句執行方面出現了狀況,幫助我們排查線上環境中的問題哦。通過這個實際的復雜查詢案例,大家可以更直觀地體會到這三種打印 SQL 語句方法在實際開發中的應用場景和各自的特點,從而根據具體的開發需求和環境,選擇最適合的方法來幫助我們進行調試和優化工作啦。
五、總結與注意事項
通過以上內容,我們詳細介紹了 Laravel 中打印 SQL 語句的多種方法,包括開啟執行日志法、toSql() 方法以及在 AppServiceProvider 中處理的方法,并對它們的使用便捷性、獲取信息完整性和性能影響等方面進行了對比分析,同時展示了在實際復雜查詢案例中的應用。在實際使用過程中,大家需要注意以下幾點:首先,在生產環境中,應謹慎使用開啟執行日志法來打印 SQL 語句,因為它可能會對性能產生較大的影響,導致系統響應變慢等問題。而在開發和測試環境中,這種方法對于排查復雜業務邏輯中的 SQL 執行情況非常有幫助,可以根據實際情況合理運用。其次,使用 toSql() 方法時,要清楚它獲取的 SQL 語句中參數是用 ? 占位符表示的,如果需要完整的帶有實際參數值的 SQL 語句,還需要進行額外的處理操作,避免在不知情的情況下因為參數問題導致對 SQL 語句的理解和調試出現偏差。對于在 AppServiceProvider 中處理的方法,雖然它是一種較為穩定和實用的方法,但在配置代碼時要確保準確無誤,以免出現記錄的 SQL 語句信息不準確或者無法記錄的情況。另外,無論使用哪種方法打印 SQL 語句,都要注意代碼的安全性和性能優化。避免在不必要的地方頻繁打印 SQL 語句,同時要確保打印的 SQL 語句不會泄露敏感信息,防止潛在的安全風險??傊莆?Laravel 打印 SQL 語句的方法,能夠幫助我們更好地進行數據庫查詢的調試和優化,提高項目的開發效率和質量,確保項目的穩定運行。希望大家在實際開發中,根據項目的具體需求和環境,靈活選擇合適的方法來打印 SQL 語句,為項目的順利推進提供有力的支持。