2.1 多表查詢
2.1.1 多表查詢的基本語法(重點)
多表查詢語法如下:
SELECT {DISTINCT} *|查詢列1 別名1,查詢列2 別名2 FROM 表名稱1 別名1,表名稱2 別名2 {WHERE 條件(s)} {ORDER BY 排序字段 ASC|DESC,排序字段 ASC|DESC} |
例:同時查詢emp和dept表
SQL> select * from emp,dept; |
發現返回了56條數據,emp一個才14條,dept表才4條。56=14*4。
查詢emp表的記錄數:
SQL> select count(*) from emp; |
說明使用多表查詢的時候會產生笛卡爾積,要想去掉笛卡爾積,必須使用字段進行關聯操作。
在emp和dept表中都有一個deptno字段,屬於關聯字段。
例:加入where語句消除笛卡爾積
Select * from emp,dept Where emp.deptno=dept.deptno; |
例:查詢雇員的編號、姓名、部門編號、部門名稱即部門位置。
select e.deptno,e.ename,e.deptno,d.dname,d.loc from emp e,dept d where e.deptno=d.deptno; |
例:查詢每個雇員的姓名、工作、雇員的直接上級領導的姓名
select e.ename name,e.job job,m.ename mgr from emp e,emp m where e.mgr=m.empno; |
例:要求繼續擴展,將部門名稱也列出來。
select e.ename name,e.job job,m.ename mgr,d.dname from emp e,emp m,dept d where e.mgr=m.empno and e.deptno=d.deptno; |
思考:要求查詢出每個雇員的姓名、工資、部門名稱,工資在公司的等級,及其領導的姓名及工資等級。
select e.ename name, e.sal sal,d.dname dname,s.grade grade,m.ename mgr,ms.grade from emp e,dept d,salgrade s,emp m,salgrade ms where (e.deptno=d.deptno )and ( e.sal between s.losal and s.hisal) and ( e.mgr=m.empno) and (m.sal between ms.losal and ms.hisal); |
進一步擴展:要求按照以下格式顯示工資等級
l 1:第五等工資
l 2:第四等工資
l 3:第三等工資
l 4:第二等工資
l 5:第一等工資
select e.ename name,e.sal ,d.dname dname, decode(s.grade,1,'第五等工資',2,'第四等工資',3,'第三等工資',4,'第二等工資' ,5,'第一等工資' ) grade,m.ename mgr, decode(ms.grade,1,'第五等工資',2,'第四等工資',3,'第三等工資',4,'第二等工資' ,5,'第一等工資' ) mgrade from emp e,dept d,salgrade s,emp m,salgrade ms where (e.deptno=d.deptno )and ( e.sal between s.losal and s.hisal) and ( e.mgr=m.empno) and (m.sal between ms.losal and ms.hisal);
|
2.1.2、左、右連接(重點)
Dept表中有4條數據
將emp和dept關聯查詢,查詢發現只寫出3個部門,因為雇員表中並沒有40部門的雇員。
select e.ename ,e.deptno,d.dname from emp e,dept d where e.deptno(+)=d.deptno; |
可以發現40部門出現了。這里用到了右連接,具有以下規律:
l (+)在=左邊表示右連接
l (+)在=右邊表示左連接
select e.ename ,e.deptno,d.dname from emp e,dept d where e.deptno=d.deptno(+); |
發現以上的(+)沒起到作用,無法顯示40部門信息。
左、右連接在一般在開發使用較多,實際上之前的查詢雇員姓名及每個雇員領導的時候就應該用到左、右連接。
e.mgr = m.empno(+) e.mgr = m.empno
select e.empno,e.ename ,m.ename,m.empno from emp e,emp m where e.mgr = m.empno(+); |
2.1.3 SQL:1999語法對SQL的支持(了解)
SQL:1999語法格式:
SELECT table1.column,table2.column FROM table1 [CROSS JOIN table2]| [NATURAL JOIN table2]| [JOIN table2 USING(column_name)]| [JOIN table2 ON(table1.column_name=table2.column_name)]| [LEFT|RIGHT|FULL OUTER JOIN table2 ON(table1.column_name=table2.column_name)]; |
例:交叉連接(CROSS JOIN):產生笛卡爾積
select * from emp cross join dept; |
例:自然連接(NATURAL JOIN):自動進行關聯字段的匹配。
select * from emp natural join dept; |
例:USING子句:直接關聯的操作列
select * from emp e join dept d using(deptno) where deptno=30; |
例:ON子句,用戶自己編寫的連接條件。
SELECT * FROM emp e JOIN dept d ON(e.deptno=d.deptno) WHERE e.deptno=30 ; |
例:左連接(左外連接)、右連接(右外連接):LEFT JOIN,RIGHT JOIN
select e.ename,d.deptno,d.dname,d.loc from emp e right outer join dept d on (e.deptno=d.deptno); |
2.2 組函數及分組統計(重點)
2.2.1 組函數
在SQL中常用的組函數有以下幾個:
l COUNT():求出全部的記錄數
l MAX():求出一組中的最大值
l MIN():求出最小值
l AVG():求出平均值
l SUM():求和
例:
Select count(empno)from emp; |
Select min(sal) from emp; |
例:求20部門的總工資
Select sum(sal) from emp where deptno=20; |
2.2.2 分組統計
分組統計需要用GROUPBY進行分組,SQL語法格式:
SELECT {DISTINCT} * |列1 別名1,列2 別名2 FROM 表1 別名1,表2 別名2 {WHERE 條件(s)} {GROUP BY 分組條件} {ORDER BY 排序字段 ASC|DESC , 排序字段ACS | DESC ,…} |
例:求出每個部門雇員總數。
select deptno, count(empno) from emp group by (deptno); |
注意點:觀察以下代碼
SELECT deptno,COUNT(empno) FROM emp ; |
以上代碼不能正確執行,是因為:
1 如果程序中使用了分組函數,則有兩種可以使用的情況:
n 程序中存在了GROUP BY,並指定了分組條件,這樣可以將分組條件一起查詢出來;
n 如果不使用分組的話,則只能單獨的使用分組函數。
2 在使用分組函數的時候,不能出現分組函數和分組條件以外的字段。
SELECT deptno,empno,COUNT(empno) FROM emp GROUP BY deptno ; |
例:按部門分組,並顯示部門的名稱,及每個部門的員工數。
select count(e.empno) ,d.dname from emp e,dept d where e.deptno=d.deptno group by d.dname; |
例:求出平均工資大於2000的部門編號和平均工資
select deptno,avg(sal) from emp where avg(sal)>2000 group by deptno; |
分組函數只能在分組中使用,不允許在where語句中出現。如果現在研指定分組發條件。則只能通過第二種條件的指令:HAVING,語法格式:
SELECT {DISTINCT} * 列1 別名1,列2 別名2 FROM 表1 別名1,表2 別名2 {WHERE 條件(s)} {GROUP BY 分組條件{HAVING分組條件}} {ORDER BY 排序字段ASC|DESC , 排序字段 ACS | DESC ,…} |
例:使用HAVING完成以上操作
select deptno,avg(sal) from emp group by deptno HAVING avg(sal) >2000; |
例:顯示非銷售人員工作名稱以及從事同一個工作雇員的工資的總和,並且滿足從事同一工作的雇員的月工作合計大於5000,輸出結果按工資的合計升序排列。
select job,sum(sal) from emp where job<> 'SALESMAN' group by job having sum(sal)>5000 order by sum(sal); |
分組的簡單原則:
只要一列上存在重復的內容才有可能考慮到分組
注意:
分組函數可以嵌套使用,但是在嵌套使用的時候不能再出現分組條件的查詢語句。
例:求出平均工資最高的部門工資
錯誤代碼:
select deptno ,max(sum(sal)) from emp group by deptno; |
正確代碼:
select max(sum(sal)) from emp group by deptno; |
2.3 子查詢
子查詢:在一個查詢內部還包含另外一個查詢,格式:
SELECT {DISTINCT} * | 列1 別名1,列2 別名2 FROM 表1 別名1,表2 別名2 ( SELECT {DISTINCT} * |列1 別名1,列2 別名2 FROM表1 別名1,表2 別名2 {WHERE 條件(s)} {GROUP BY 分組條件 {HAVING分組條件}} {ORDER BY 查詢字段ASC|DESC , 查詢字段ACS | DESC , } ) 別名, … {WHERE條件(s) ( SELECT {DISTINCT} * |列1 別名1,列2 別名2 FROM表1 別名1,表2 別名2 {WHERE條件(s)} {GROUP BY分組條件{HAVING分組條件}} {ORDER BY 查詢字段ASC|DESC , 查詢字段ACS | DESC , } ) } {GROUP BY {HAVING }} {ORDER BY ASC|DESC , ACS | DESC , } |
例:要求查詢出比7654工資高的全部雇員信息
l 首先要知道7654的工資是多少
select sal s1 from emp where empno=7654; |
l 之后要以以上的結果作為后續查詢的依據,只有是其他的工作大於s1,則符合條件。
select * from emp where sal>(select sal s1 from emp where empno=7654); |
所有子查詢必須在()中編寫代碼。
子查詢在操作中又分為以下三類:(面試題)
l 單列子查詢:返回的結果是一列的一個內容,出現幾率高;
l 單行子查詢:返回多個列。有可能是一條完整的記錄
l 多行子查詢:返回多條記錄
例:要求查詢出工資比7654高,同時與7788從事相同工作的全部雇員信息。
select * from emp where sal>(select sal s1 from emp where empno=7654) and job=(select job from emp where empno=7788); |
思考:
要求查詢出:部門名稱、部門的員工數,部門的平均工資,部門的最低收入雇員的姓名。
1、求出每個部門的員工數、平均工資
select deptno,count(deptno),avg(sal) from emp group by deptno; |
2、查詢部門的名稱,需關聯dept表
select d.dname,ed.deptno,ed.cnt,ed.arg from dept d,( select deptno,count(deptno) cnt,avg(sal) arg from emp group by deptno) ed where d.deptno=ed.deptno; |
3、求出最低收入的雇員姓名
select d.dname,ed.deptno,ed.cnt,ed.arg,ed.m,e.ename from dept d,emp e, (select deptno,count(deptno) cnt,avg(sal) arg,min(sal) m from emp group by deptno) ed where d.deptno=ed.deptno and e.sal= ed.m; |
如果此時在一個部門中同時存在兩個工資最低的雇員,則程序就會出錯。在子查詢中存在以下三種查詢的操作符號:
l IN:指定一個查詢的范圍
l ANY:
=ANY:與IN的操作符功能完全一樣
>ANY:比里面最小的值要大
<ANY: 比最大的值要小
l ALL
>ALL:比最大值要大的
<ALL:比最小值要小的
例:求出每個部門的最低工資的雇員信息:IN
有多個部門,因此返回的值肯定的多個,此時可以用IN指定一個操作范圍。
select * from emp where sal IN (select min(sal) from emp group by deptno); |
例:ANY操作
l =ANY: 與IN的操作符功能完全一樣
select * from emp where sal =ANY (select min(sal) from emp group by deptno); |
l >ANY: 比里面最小的值要大(所有工資大於800的),共13條數據
select * from emp where sal >ANY (select min(sal) from emp group by deptno); |
l <ANY: 比最大的值要小(所有工資小於1300的)
select * from emp where sal <ANY (select min(sal) from emp group by deptno); |
ALL操作
l >ALL:比最大值要大的(所有工資大於1300的雇員的信息)
l <ALL:比最小值要小的(所有工資小於800的雇員的信息)
select * from emp where sal >ALL (select min(sal) from emp group by deptno); |
對於子查詢來講,還可以進行多列子查詢,一個子查詢中同時返回多個查詢的列。
select * from emp where (sal,NVL(comm,-1)) IN( select sal,NVL(comm,-1) from emp where deptno=20); |
2.4 數據庫更新操作
數據庫的主要操作分為兩種:
l 數據庫的查詢操作:SELECT
l 數據庫的更新操作:INSERT(添加)、UPDATE(修改,更新)、DELETE(刪除)
復制一份emp表的信息:
create table myemp as select * from emp; |
2.4.1 添加數據
添加數據的語法:
INSERT INTO 表名稱[(字段名稱1,字段名稱2,…)]VALUES(值1,值2,…); |
例:為myemp表添加一條數據
l 使用標准的做法完成(推薦)
insert into myemp (empno,ename,job,mgr,hiredate,sal,comm,deptno) values(7899,'張三','清潔工',7369,'14-2月-1995',500,300,40); |
l 使用簡略的寫法(不推薦)。因為要添加所有字段的內容,所有可以不寫上字段名稱,只有值的數量及順序與數據表中的一致即可。
insert into myemp values(8899,'李四','清潔工',7369,'14-2月-1995',510,300,40); |
例:要求插入一個新雇員,但是該雇員沒有領導,也沒有獎金。
l 第一種做法:明確寫出要插入的字段
insert into myemp (empno,ename,job,hiredate,sal,deptno) values(3242,'王五','清潔工','13-6月-2000',5400,40); |
l 第二種做法:如果插入時沒有明確寫出字段名稱的話,使用null表示不插入的字段的具體內容。
insert into myemp values(3242,'王五','清潔工',null,'13-6月-2000',5400,null,40); |
之前插入數據的時候,日期的格式是按照表中固定的格式,如果現在的日期格式的“2014-01-14”的話,那么就要用TO_DATE()函數,將字符串類型變為DATE類型數據。
Insert into myemp values(8888,'趙六','清潔工',null,to_date('2014-01-14','yyyy-mm-dd'),6000,null,40); |
2.4.2 修改數據
在SQL中使用UPDATE語句可以完成數據的修改功能,語法格式:
修改全部:UPDATE 表名稱 SET 要修改的字段 = 新值,要修改的字段 = 新值,… 修改局部:UPDATE 表名稱 SET 要修改的字段 = 新值,要修改的字段 = 新值,…WHERE 修改條件 |
從一般的開發角度上講,修改操作一般都是加入修改條件。
例:將myemp表中所有雇員的佣金修改為1000——>修改全部
update myemp set comm=1000; |
例:將編號為7899的雇員的工作修改為7000——>指定修改條件
update myemp set sal=7000 where empno=7899; |
例:將7369、8899、7788的領導及獎金取消
Update myemp set mgr =null,comm=null where empno in(7369,8899,7788); |
2.4.3 刪除數據
在SQL中使用DELETE命令刪除記錄,語法格式如下:
刪除全部:DELETE FROM 表名稱; 刪除局部:DELETE FROM 表名稱 WHERE 刪除條件; |
例:刪除編號為7899的雇員信息
Delete from myemp where empno=7899; |
例:刪除表的全部內容
Delete from myemp; |
2.5 事務處理
例:建一個只含10部門雇員的臨時表
Create table emp10 as select * from emp where deptno=10; |
刪除emp10表中7782的雇員后,再查詢發現已經將此雇員信息刪除掉了,但是選擇打開第二個窗口,再次查詢emp10,發現7782還存在。實際上這就是Oracle中事務處理的概念。
事務處理:保證數據操作的完整性,所有的操作要么同時成功,要么同時失敗。
在Oracle中對於每個連接到數據庫的窗口(sqlplus、sqlplusw)連接之后實際上都會與數據庫的連接建立一個session,即:每一個連接到數據庫上的用戶都表示創建了一個session。
一個是session對數據庫所做的修改,不會立刻反應到數據庫的真實數據上,是允許回滾的,當一個session提交所有的操作之后,數據庫才真正的做出修改。
n 提交事務:commit
n 回滾事務:rollback;
|-如果數據已經被提交了,則肯定無法回滾。
在Oracle中關於事務的處理上也會存在一種死鎖的概念。
n 一個session如果更新了數據庫中的記錄,其他session是無法立刻更新的,要等待對方提交之后才允許更新。
2.6 查詢練習
1、列出所有至少有一個員工的部門。
select dn.deptno,d.dname from dept d,( select deptno from emp group by deptno having count(deptno) > 0) dn where d.deptno=dn.deptno; |
2、列出所有薪水比“SMITH”高的員工。
select * from emp where sal >( select sal from emp where ename='SMITH'); |
3、列出所有員工的姓名及直接上級的姓名
select e.ename name, m.ename mgr from emp e,emp m where e.mgr=m.empno; |
4、列出所有受雇日期早於直接上級的員工的編號,姓名,部門名稱
select e.empno,e.ename,e.hiredate,m.hiredate,d.dname from emp e,emp m,dept d where e.mgr=m.empno and e.hiredate -m.hiredate<0 and d.deptno=e.deptno; |
5、列出部門名稱和這些部門的員工信息,同時列出沒有員工的部門。
select d.dname,d.deptno,e.empno,e.ename from dept d,emp e where d.deptno=e.deptno(+); |
6、列出所有工作為’CLERK’的姓名及其部門名稱,部門的人數。
select e.ename n,d.dname d,j.c from emp e,dept d,(select deptno,count(deptno) c from emp group by deptno) j where job='CLERK' and d.deptno=e.deptno and j.deptno=e.deptno ; |
7、求出最低工資大於1500的各種工作及其從事此工作的全部雇員人數。
select job ,min(sal), count(empno) from emp group by job having min(sal)>1500; |
select e.job,count(e.empno) from emp e where e.job IN ( select job from emp group by job having min(sal)>1500) group by e.job; |
8、列出在部門’SALES’工作的員工的姓名,假定不知道銷售部門的編號。
select e.ename,e.deptno,d.dname from emp e,dept d where e.deptno=d.deptno and d.dname='SALES'; |
select ename from emp where deptno=(select deptno from dept where dname='SALES'); |
9、列出薪水高於公司平均薪水的所有員工,所在部門,上級領導,工資等級。
select e.deptno,e.ename,e.sal,d.dname,d.loc,m.ename,g.grade from emp e,dept d,emp m,salgrade g where e.sal > (select avg(sal) from emp) and e.deptno=d.deptno and e.mgr=m.empno(+) and e.sal between g.losal and g.hisal; |
10、列出與’SCOTT’從事相同工作的所有員工及其部門名稱。
select e.* ,d.dname from emp e,dept d where job=(select job from emp where ename='SCOTT') and e.deptno=d.deptno and ename<>'SCOTT'; |
11、列出薪資等於部門30中員工薪資的所有員工的姓名和薪資
select ename,sal,deptno from emp where sal IN ( select sal from emp where deptno=30) and deptno<>30; |
12、列出薪資高於部門30工作的所有員工的薪資的員工姓名和薪資、部門
select e.ename,e.sal,e.deptno,d.dname from emp e,dept d where sal >ALL ( select sal from emp where deptno=30) and e.deptno=d.deptno and e.deptno <>30; |
13、列出在每個部門工資的員工數量、平均工資和平均服務期限。
select d.dname,e.* from dept d,(select deptno,count(empno) con,avg(sal), round(avg(months_between(sysdate,hiredate))/12,2) year from emp group by deptno) e where d.deptno=e.deptno; |
select d.dname,count(e.empno),avg(sal),avg(months_between(sysdate,hiredate)/12) year from emp e,dept d where d.deptno=e.deptno group by d.dname; |
14、列出所有部門的詳細信息和部門人數
select d.*,ed.c from dept d,(select deptno,count(deptno) c from emp group by deptno) ed where d.deptno=ed.deptno; |
但是以上的查詢中沒有包含部門40,因為部門40沒有任何雇員,所有部門40的員工人數應該使用0表示。
select d.*,NVL(ed.c,0) from dept d,(select deptno,count(deptno) c from emp group by deptno) ed where d.deptno=ed.deptno(+); |
15、列出各種工作的最低工資及從事此工作的雇員姓名。
select e* from emp e,(select job ,min(sal) m from emp group by job) de where e.sal=de.m and e.job=de.job; |
select * from emp where sal IN(select min(sal) from emp group by job) ; |
16、列出各個部門MANAGER的最低工資
select deptno,min(sal) from emp where job='MANAGER' group by deptno; |
17、列出所有員工的年薪,按年薪從低到高排序
select ename,job,(sal+NVL(comm,0))*12 ysal from emp order by ysal asc; |
18、求出部門名稱中帶有’S’字符的部門員工的總工資、部門人數
select d.dname,sum(e.sal),count(e.deptno) from emp e,dept d where e.deptno = d.deptno and d.dname like'%S%' group by d.dname; |
select deptno,sum(sal),count(empno) from emp where deptno IN(select deptno from dept where dname like'%S%') group by deptno; |
19、改任職超過30年的員工加薪10%
update emp set sal=1.1*sal where months_between(sysdate,hiredate)/12 > 30; |