所有工程師都曾面對這個噩夢。其原因多數是 “不知不覺中讓DB痛苦的SQL”。
本文將探討在實務上特別造成重大影響的 5種致命SQL反模式,以及額外的 批次處理優化(批次INSERT)、EXISTS 與 IN 的差異,並且整理出以下幾點:
以技術書的水準清晰地整理。
EXPLAIN 的實務確認流程索引是根據 欄位的原始值 來建立的。在 WHERE 子句中對欄位使用函數或運算時,DB無法使用索引。
WHERE YEAR(created_at) = 2024;
問題點: created_at 的索引失效 → 對所有資料應用函數 → 全表掃描
WHERE created_at >= '2024-01-01'
AND created_at < '2025-01-01';
優點: 不經過處理即可比較欄位值,從而充分利用索引。
實務檢查清單
取得父表後,在結果的迴圈內個別取得子表的模式。
結果: 1 + N 查詢。如果使用者有1000條記錄,則發出1001條查詢。
當 JOIN 變得龐大時,數據傳輸量和記憶體消耗會增加(寬JOIN問題)。
實務檢查清單
SELECT * FROM products;
問題點:
SELECT id, name, price FROM products;
實務檢查清單
WHERE name LIKE '%tanaka%';
問題點: B-Tree索引無法使用,全表掃描發生。
WHERE name LIKE 'tanaka%';
或引入全文搜索引擎(Elasticsearch、Solr、PGroonga)。
權衡
實務檢查清單
逐行INSERT大量資料時,每次INSERT都需要進行一次事務處理,導致速度極慢。
INSERT INTO logs(message) VALUES ('a');
INSERT INTO logs(message) VALUES ('b');
INSERT INTO logs(message) VALUES ('c');
問題點:
INSERT INTO logs(message) VALUES
('a'),
('b'),
('c');
或利用DB/ORM提供的批次API。
實務檢查清單
IN 會 對左側表的每一行搜尋右側的列表。
EXISTS 則僅 評估右側表中是否有符合的行。
SELECT * FROM users
WHERE id IN (SELECT user_id FROM posts);
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM posts p WHERE p.user_id = u.id
);
實務檢查清單
EXPLAIN 的實務確認流程EXPLAIN ANALYZE SELECT id, name FROM products WHERE created_at >= '2024-01-01';
-- 範例輸出(摘要)
Index Scan using idx_products_created_at on products (cost=0.29..8.52 rows=10 width=64) (actual time=0.10..0.15 rows=12 loops=1)
檢查清單
理解SQL背後發生了什麼,是成為強大工程師的必備技能。透過避免本文中的NG模式,並掌握批次處理和EXISTS的分別,可以顯著改善應用性能。
EXPLAIN (ANALYZE, BUFFERS) 🔥 最後:如果這篇文章對你有幫助
按讚、標記、追蹤 會是我的最大鼓勵!
未來也會持續發佈對初學者及資深人士有幫助的文章!!
我已經發佈了新文章!希望你會閱讀!
原文出處:https://qiita.com/Shiro_Shihi/items/b1b582c4528c5dd802f5