Oracle多表查詢、子查詢實戰練習
一、基礎練習:
1.查詢和scott相同部門的員工姓名ename和僱用日期hiredate
SELECT ENAME,HIREDATE FROM EMP WHERE DEPTNO=(SELECT DEPTNO FROM EMP WHERE ENAME='SCOTT');
2.查詢在部門的loc為NEW YORK的部門工作的員工的員工姓名ename,部門名稱dname和崗位名稱job
SELECT E.ENAME,D.DNAME,E.JOB,D.LOC FROM EMP E,DEPT D WHERE E.DEPTNO=D.DEPTNO AND D.LOC='NEW YORK';
3.查詢上司是king的員工姓名(ename)和工資(sal)
SELECT ENAME,SAL FROM EMP WHERE MGR=(SELECT EMPNO FROM EMP WHERE ENAME='KING');
4.查詢與姓名中包含字母U的員工在相同部門的員工資訊
SELECT * FROM EMP WHERE DEPTNO IN(SELECT DEPTNO FROM EMP WHERE ENAME LIKE '%U%');
5.查詢所有僱員姓名和部門名稱(使用left join,inner join, right join)
SELECT E.ENAME,D.DNAME FROM EMP E INNER JOIN DEPT D ON E.DEPTNO=D.DEPTNO;
SELECT E.ENAME,D.DNAME FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO=D.DEPTNO;
SELECT E.ENAME,D.DNAME FROM DEPT D RIGHT JOIN EMP E ON E.DEPTNO=D.DEPTNO;
6.顯示每個員工的員工姓名、部門名稱、職務、工資、和工資等級資訊(使用left join,inner join, right join)
SELECT E.ENAME,D.DNAME,E.JOB,E.SAL,S.GRADE FROM EMP E INNER JOIN DEPT D ON E.DEPTNO=D.DEPTNO INNER JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL;
SELECT E.ENAME,D.DNAME,E.JOB,E.SAL,S.GRADE FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO=D.DEPTNO LEFT JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL;
SELECT E.ENAME,D.DNAME,E.JOB,E.SAL,S.GRADE FROM DEPT D RIGHT JOIN EMP E ON E.DEPTNO=D.DEPTNO RIGHT JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL;
二、綜合練習
1.取得每個部門最高薪水的人員名稱2.列出受僱日期早於其直接上級的所有員工的編號,姓名,部門名稱
--使用相關子查詢 SELECT EMPNO,ENAME,SAL,DEPTNO FROM EMP E WHERE E.SAL=(SELECT MAX(SAL) FROM EMP M WHERE M.DEPTNO=E.DEPTNO) ORDER BY DEPTNO; --使用多表連線查詢(漁舟唱晚同學的) SELECT EMPNO,ENAME,SAL,DEPTNO FROM EMP Q, (SELECT E.DEPTNO 部門, MAX(E.SAL) 最高薪資 FROM EMP E GROUP BY E.DEPTNO) R WHERE R.部門 = Q.DEPTNO AND Q.SAL = R.最高薪資 ORDER BY Q.DEPTNO; --使用DENSE_RANK()函式結合ORDER BY SELECT * FROM(SELECT EMPNO,ENAME,SAL,DEPTNO,DENSE_RANK() OVER(PARTITION BY DEPTNO ORDER BY SAL DESC)RN FROM EMP) WHERE RN=1 ORDER BY DEPTNO; --使用IN子查詢(有BUG) SELECT EMPNO,ENAME,DEPTNO,SAL FROM EMP WHERE SAL IN(SELECT MAX(SAL) FROM EMP GROUP BY DEPTNO) ORDER BY DEPTNO;
--測試上面的IN子查詢BUG:發現10部門的NulluN也顯示出來了,但其並非10部門最高工資,10部門最高工資為5000
INSERT INTO EMP(EMPNO,ENAME,DEPTNO,SAL) VALUES(1015,'NulluN',10,3000);
SELECT EMPNO,ENAME,DEPTNO,SAL FROM EMP WHERE SAL IN(SELECT MAX(SAL) FROM EMP GROUP BY DEPTNO) ORDER BY DEPTNO;
2.列出受僱日期早於其直接上級的所有員工的編號,姓名,部門名稱
--左自連線和多表查詢
SELECT E.EMPNO 員工編號,E.ENAME 員工姓名,M.ENAME 主管姓名,E.HIREDATE 員工受僱日期, M.HIREDATE 上級僱用日期,D.DNAME 部門名稱
FROM EMP E,EMP M,DEPT D WHERE M.EMPNO(+)=E.MGR AND E.HIREDATE<M.HIREDATE AND E.DEPTNO=D.DEPTNO ORDER BY E.EMPNO;
--相關子查詢和多表查詢 SELECT E.EMPNO,E.ENAME,D.DNAME FROM EMP E,DEPT D WHERE E.HIREDATE <(SELECT HIREDATE FROM EMP M WHERE M.EMPNO=E.MGR) AND E.DEPTNO=D.DEPTNO ORDER BY E.EMPNO;
3.列出所有"CLERK"(辦事員)的姓名及其部門名稱,部門的人數
思路:1.先查詢JOB為CLERK的所有部門編號,將該子查詢結果命名為A;2.再從EMP表查詢與A查詢中部門編號相同的員工所在的部門人數,這一步的查詢結果命名為B;3.最後從EMP表、DEPT表和B查詢中進行多表查詢獲取JOB為CLERK的所有員工的姓名、部門名稱和所在部門人數。
SELECT E.ENAME,D.DNAME,T.部門人數,E.JOB FROM EMP E,DEPT D,(SELECT DEPTNO,COUNT(1) 部門人數 FROM EMP WHERE DEPTNO IN(
SELECT DISTINCT DEPTNO FROM EMP WHERE JOB='CLERK') GROUP BY DEPTNO)T
WHERE E.DEPTNO=D.DEPTNO AND E.JOB='CLERK' AND T.DEPTNO=E.DEPTNO;
4.列出與"SCOTT"從事相同工作的所有員工及部門名稱
SELECT E.*,D.DNAME FROM EMP E,DEPT D WHERE E.JOB=(SELECT JOB FROM EMP WHERE ENAME='SCOTT') AND E.DEPTNO=D.DEPTNO;
5.查出某個員工的上級主管,並要求出這些主管中的薪水超過3000
SELECT E.EMPNO 員工編號,E.ENAME 員工姓名,M.ENAME 主管姓名,M.SAL 主管工資 FROM EMP E,EMP M WHERE M.EMPNO(+)=E.MGR AND M.SAL>3000;
6.找出部門10中所有經理(MANAGER)和部門20中所有辦事員(CLERK)的詳細資料
SELECT E.*,D.DNAME,D.LOC,S.* FROM EMP E,DEPT D,SALGRADE S WHERE E.DEPTNO=D.DEPTNO AND E.SAL BETWEEN S.LOSAL AND S.HISAL
AND (E.DEPTNO=10 AND E.JOB='MANAGER' OR E.DEPTNO=20 AND E.JOB='CLERK');
--注意:E.DEPTNO=10 AND E.JOB='MANAGER' OR E.DEPTNO=20 AND E.JOB='CLERK' 要用括號括起來,不然會與前面的AND條件混淆造成錯誤!
7.找出早於12年前受僱的員工. 並且按受僱年份倒序排序
思路一:用MONTHS_BETWEEN比較當前系統時間和受僱日期之前相差的月份,然後除以12,如果值大於12,則是早於12前受僱的員工。
--有錯誤的語句
SELECT E.*,TO_CHAR(HIREDATE,'YYYY') 受僱年份,ROUND((MONTHS_BETWEEN(SYSDATE,HIREDATE)/12),2) 受僱年限 FROM EMP E WHERE 受僱年限>12 ORDER BY 受僱年份 DESC;
/*為什麼“受僱年限”會是無效的識別符號呢?因為SELECT語句在WHERE語句後面才執行,而列的別名(受僱年限)是在SELECT時才生成的,故在WHERE子句中看不到這個別名(受僱年限),自然無法引用這個別名了。*/--排錯後的正確語句
SELECT E.*,TO_CHAR(HIREDATE,'YYYY') 受僱年份,ROUND((MONTHS_BETWEEN(SYSDATE,HIREDATE)/12),2) 受僱年限 FROM EMP E WHERE (MONTHS_BETWEEN(SYSDATE,HIREDATE)/12)>12 ORDER BY 受僱年份 DESC;
思路二:用ADD_MONTHS判斷,(受僱日期+12*12)得出的日期如果小於當前系統時間,則是早於12前受僱的員工。
SELECT E.*,TO_CHAR(HIREDATE,'YYYY') 受僱年份,ADD_MONTHS(HIREDATE,12*12) 受僱十二週年日,ROUND((MONTHS_BETWEEN(SYSDATE,HIREDATE)/12),2) 受僱年限 FROM EMP E
WHERE ADD_MONTHS(HIREDATE,12*12)<SYSDATE ORDER BY 受僱年份 DESC;
--注意:離當前日期越遠的日期越小,反之,離當前日期越近的日期越大。
8.列出從事同一種工作但屬於不同部門的員工的一種組合
--不算完美但算比較接近題意的SQL語句
SELECT DISTINCT E.EMPNO,E.ENAME,E.JOB,E.DEPTNO FROM EMP E,EMP P WHERE E.DEPTNO!=P.DEPTNO AND E.JOB=P.JOB ORDER BY JOB,DEPTNO;
--其它兩種不等於的寫法
SELECT DISTINCT E.EMPNO,E.ENAME,E.JOB,E.DEPTNO FROM EMP E,EMP P WHERE E.DEPTNO<>P.DEPTNO AND E.JOB=P.JOB ORDER BY JOB,DEPTNO;
SELECT DISTINCT E.EMPNO,E.ENAME,E.JOB,E.DEPTNO FROM EMP E,EMP P WHERE E.DEPTNO^=P.DEPTNO AND E.JOB=P.JOB ORDER BY JOB,DEPTNO;
/*精妙之處:使用DISTINCT!如果不使用DISTINCT,查詢結果會出現很多一樣的重複資料!*/
分析:為什麼說上面的SQL語句不算完美呢?因為從上圖可看出JOB為CLERK,且DEPTNO=20的記錄有兩條,即分別是第2和第3條查詢記錄,這就與題目要求的“從事同一種工作但屬於不同部門的員工”不一致了,故最理想的查詢結果應該如下:
9.查詢有獎金的所有員工的姓名、獎金以及所在部門名稱
--如果獎金等於0也算有獎金,那如下實現:
SELECT ENAME,COMM,DNAME FROM EMP E,DEPT D WHERE COMM IS NOT NULL AND E.DEPTNO=D.DEPTNO;
--如果獎金等於0不算有獎金,則如下實現:
SELECT ENAME,COMM,DNAME FROM EMP E,DEPT D WHERE COMM IS NOT NULL AND COMM<>0 AND E.DEPTNO=D.DEPTNO;
10.給任職日期超過25年的員工加薪10%
SELECT E.ENAME,E.SAL 原薪水,E.SAL*1.1 加薪後薪水,ROUND((MONTHS_BETWEEN(SYSDATE,HIREDATE)/12),2) 受僱年限 FROM EMP E
WHERE (MONTHS_BETWEEN(SYSDATE,HIREDATE)/12)>25;
Linux公社的RSS地址 : ofollow,noindex" target="_blank">https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址: https://www.linuxidc.com/Linux/2018-10/154914.htm