WHERE 在分組前過濾列,HAVING 在分組後過濾組。能用 WHERE 解決的絕不用 HAVING。
-- Bad
SELECT product_type_id, AVG(price)
FROM products
GROUP BY product_type_id
HAVING product_type_id IN (1,2);
-- Good
SELECT product_type_id, AVG(price)
FROM products
WHERE product_type_id IN (1,2)
GROUP BY product_type_id;
單次 JOIN 替代多次往返資料庫,減少網路交互與解析開銷。
-- Good: p.description, p.price 明確指定來源表
SELECT p.name, pt.name, p.description, p.price
FROM products p, product_types pt
WHERE p.product_type_id = pt.product_type_id
AND p.product_id = 1;
SELECT COUNT(CASE WHEN price < 13 THEN 1 END) AS low,
COUNT(CASE WHEN price BETWEEN 13 AND 15 THEN 1 END) AS med,
COUNT(CASE WHEN price > 15 THEN 1 END) AS high
FROM products;
UNION 需排序去重,UNION ALL 直接合併,效能差異顯著。結果確無重複或可容忍重複時用 ALL。
-- Good: 半連結,找到一筆即停止
SELECT product_id, name
FROM products p
WHERE EXISTS (SELECT 1 FROM purchases pu WHERE pu.product_id = p.product_id);
-- Bad: 需排序去重
SELECT DISTINCT pr.product_id, pr.name
FROM products pr, purchases pu
WHERE pr.product_id = pu.product_id;
-- Good
SELECT pr.product_id, pr.name
FROM products pr
WHERE EXISTS (SELECT 1 FROM purchases pu WHERE pu.product_id = pr.product_id);
VARIABLE pid NUMBER
EXEC :pid := 1;
SELECT * FROM products WHERE product_id = :pid;
EXEC :pid := 2;
SELECT * FROM products WHERE product_id = :pid; -- 共享同一游標
注意:綁定變數屬 Session 級,重連需重新宣告。SQL 文字必須完全相同 (含大小寫、空白) 才能共享。
-- Bad: 索引失效
WHERE TRUNC(create_date) = SYSDATE
WHERE salary * 12 > 100000
-- Good: 等價改寫保留索引
WHERE create_date >= TRUNC(SYSDATE) AND create_date < TRUNC(SYSDATE)+1
WHERE salary > 100000/12
DBMS_STATS.GATHER_TABLE_STATS)?