1. 程式人生 > >Oracle作業5——多表查詢、子查詢

Oracle作業5——多表查詢、子查詢

一、基礎練習:

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;