許多開發者視 SQL 為難以馴服的野獸。它是少數的聲明式語言之一,與命令式、面向對象或函數式語言截然不同。本教程針對已使用 SQL 但未深入理解其本質的讀者,通過十個核心步驟,重塑你的 SQL 思維模型。
你只需聲明想要的結果特徵,無需關心計算機如何獲取。例如:
SELECT first_name, last_name FROM employees WHERE salary > 100000
你只需知道「誰薪水超過 10 萬」,無需關心數據從哪個磁塊讀取、用什麼索引、怎麼連接表。核心心法:忘掉循環、變量、控制流,專注於聲明結果集的特徵。
SQL 語法順序:SELECT → FROM → WHERE → GROUP BY → HAVING → UNION → ORDER BY
但邏輯執行順序:FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → UNION → ORDER BY
三大陷阱:
WHERE z = 10 無法引用 SELECT x+y AS z。解決辦法:重複表達式、使用派生表/CTE/視圖。FROM 子句輸出是「表引用」的組合。FROM a, b 產生笛卡爾積(a列數+b列數,a行數×b行數)。所有後續子句(WHERE、GROUP BY)都在這個虛擬表上操作。始終從表引用角度思考數據流向。
JOIN 不是 SELECT 的一部分,而是構造「連接表」的關鍵字。<table reference> ::= <table name> | <derived table> | <joined table>。理解這點能讓你靈活構建複雜查詢,甚至在 INSERT/UPDATE/DELETE 中使用 JOIN。
FROM a, b WHERE a.id=b.id 易漏寫連接條件、難以區分內外連接。显式 JOIN ... ON ... 更安全、更具表現力、更易維護。
IN 或 EXISTS 實現,「只關心左表是否有匹配」。避免用 DISTINCT 去重,性能差且邏輯易錯。NOT IN / NOT EXISTS,「找沒有匹配的」。子查詢包裹在括號中即為派生表。善用它可在 SELECT/WHERE 間復用表達式:
SELECT * FROM ( SELECT first_name, last_name, current_date - date_of_birth AS age FROM author ) a WHERE age > 10000
更現代的寫法是 CTE(WITH a AS (...) SELECT ...),可重複引用,代碼更清晰。
GROUP BY 將原表引用轉換為新表引用,僅保留分組列與聚合列。MySQL 允許非標準寫法(未分組列直接 SELECT),但會導致結果不可預測,請遵循標準。
關系代數中 SELECT 稱「投影」。它在所有表變換完成後,才對列進行運算、函數調用。規則複雜:有 GROUP BY 只能用分組列/聚合函數;無 GROUP BY 可用窗口函數;不能混合聚合與非聚合函數等。策略:先搞定 FROM/WHERE/GROUP BY/JOIN,最後再寫 SELECT。
UNION ALL 保留重複。OFFSET/FETCH(或 MySQL LIMIT)分頁。無 ORDER BY 的順序不可靠。大量練習,從常見錯誤中學習。推薦閱讀《10 Common Mistakes Java Developers Make when Writing SQL》系列。掌握這十步,日寫 SQL 將更有章法,查詢更高效、可維護。