多表查詢是 SQL 核心技能。本文精選 19 個經典場景,涵蓋**內連接、外連接、自連接、子查詢、聚合分組、集合運算、相關子查詢**等模式,每例附帶業務語意、SQL 寫法與結果解析,作為開發參考與面試複習資料。範例基於經典 SCOTT.EMP/DEPT 架構。
-- EMP: EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
-- DEPT: DEPTNO, DNAME, LOC
SELECT deptno, dname, loc
FROM dept
WHERE deptno IN (SELECT deptno FROM emp);
-- 或 EXISTS 寫法,效能通常更佳
SELECT deptno, dname, loc
FROM dept d
WHERE EXISTS (SELECT 1 FROM emp e WHERE e.deptno = d.deptno);
SELECT * FROM emp
WHERE sal > (SELECT sal FROM emp WHERE ename = 'SMITH');
SELECT e.ename AS emp_name, m.ename AS mgr_name
FROM emp e
JOIN emp m ON e.mgr = m.empno;
SELECT e.ename, e.hiredate AS emp_hire, m.ename, m.hiredate AS mgr_hire
FROM emp e
JOIN emp m ON e.mgr = m.empno
WHERE e.hiredate < m.hiredate;
-- Oracle 舊語法 (+)
SELECT d.dname, e.*
FROM emp e, dept d
WHERE e.deptno(+) = d.deptno;
-- ANSI SQL-92 標準 (推薦)
SELECT d.dname, e.*
FROM dept d
LEFT JOIN emp e ON e.deptno = d.deptno;
SELECT e.ename, d.dname
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE e.job = 'CLERK';
SELECT job
FROM emp
GROUP BY job
HAVING MIN(sal) > 1500;
SELECT ename
FROM emp
WHERE deptno = (SELECT deptno FROM dept WHERE dname = 'SALES');
SELECT *
FROM emp
WHERE sal > (SELECT AVG(sal) FROM emp);
SELECT *
FROM emp
WHERE job IN (SELECT job FROM emp WHERE ename = 'SCOTT');
SELECT ename, sal
FROM emp
WHERE sal IN (SELECT sal FROM emp WHERE deptno = 30);
SELECT ename, sal
FROM emp
WHERE sal > (SELECT MAX(sal) FROM emp WHERE deptno = 30);
SELECT d.dname,
COUNT(e.empno) AS emp_cnt,
ROUND(AVG(e.sal),2) AS avg_sal,
ROUND(AVG(MONTHS_BETWEEN(SYSDATE, e.hiredate)/12),1) AS avg_years
FROM dept d
LEFT JOIN emp e ON e.deptno = d.deptno
GROUP BY d.dname;
SELECT e.ename, d.dname, (NVL(e.comm,0) + e.sal) AS total_pay
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
SELECT a.ename, a.deptno, b.ename, b.deptno, a.job
FROM emp a
JOIN emp b ON a.job = b.job AND a.deptno != b.deptno;
SELECT d.*,
(SELECT COUNT(*) FROM emp e WHERE e.deptno = d.deptno) AS emp_cnt
FROM dept d;
SELECT job, MIN(NVL(comm,0) + sal) AS min_total_pay
FROM emp
GROUP BY job;
SELECT deptno, MIN(sal) AS min_mgr_sal
FROM emp
WHERE job = 'MANAGER'
GROUP BY deptno;
SELECT ename, (NVL(comm,0) + sal) * 12 AS annual_sal
FROM emp
ORDER BY annual_sal ASC; -- 或 ORDER BY 2 ASC
-- 13 使用窗口函數避免重複掃描
SELECT DISTINCT
d.dname,
COUNT(e.empno) OVER (PARTITION BY d.deptno) AS emp_cnt,
ROUND(AVG(e.sal) OVER (PARTITION BY d.deptno),2) AS avg_sal
FROM dept d
LEFT JOIN emp e ON e.deptno = d.deptno;
-- 16 使用窗口函數
SELECT DISTINCT
d.*,
COUNT(e.empno) OVER (PARTITION BY d.deptno) AS emp_cnt
FROM dept d
LEFT JOIN emp e ON e.deptno = d.deptno;
JOIN 或 EXISTS 往往更優WHERE 對外連接欄位用 IS NULL 判斷 (改用 IS NULL 在 ON 子句)熟練掌握這 19 種模式,即可應對 90% 以上的業務多表查詢場景,並能在 Code Review 中快速識別效能隱患。