*注:此筆記爲個人在學習Oracle時從教學視頻、參考書上摘錄整理而成,純手打完成,如需轉載麻煩表明出處,附上連接(http://blog.csdn.net/sherkyoung/article/details/25988225),謝謝!
DML(數據管理語言)
一、多表查詢
1、基本概念
再之前使用的查詢操作之中,都是從一張表中進行搜索,那麼如果有一個查詢語句需
顯示多張表的數據,則就必須應用到多表查詢操作,而多表查詢操作的語法如下:
SELECT [DISTNCT] *|字段 [別名] [,字段 [別名]]
FROM 表名稱[別名][,表名稱[別名],……]
[WHERE 條件(s)]
[ORDER BY字段[ASC | DESC]][,字段[ASC | DESC]];
這與之前的操作相差不多,僅是在指定查詢的表的範圍的時候增加了需要一起查詢的表名稱或者別名。
但是如果進行多表查詢之前,必須先查詢出幾個數據——僱員表和部門表中的數據量,這個操作可以通過COUNT()函數來完成。這個函數會返回該表所含的記錄數量。
SELECT COUNT(*) FROM emp ;
SELECT COUNT(*) FROM dept ;
2、笛卡爾基
下面完成一個基本的多表查詢:
SELECT * FROM emp,dept ;
執行完這個語句之後回顯56行記錄,但是emp表只有14條記錄而dept表只有4條記錄。實際上控制檯回顯的是14*4條記錄,如圖:
上述的問題則是數據庫中的笛卡爾積即多張數據表的沉積的意思。
最簡單的解決辦法就是關聯字段的形式,而emp表和dept表之間存在關聯字段,可以從這個字段的判斷開始。
此時,需要注意的是:再多表查詢時,不同的表含有相同的字段名稱的時候訪問這些字段必須加上表名稱即變成:表.字段。
SELECT * FROM emp,dept WHERE emp.deptno=dept.deptno ;
此時的查詢結果回顯中已經不再會有笛卡爾積,但是這只是在顯示的時候進行過濾,所以在數據量比較大的時候這種方法奇差無比。
同時,在開發中常使用別名來替代表名稱:
SELECT * FROM emp e,dept d
WHERE e.deptno=ddeptno ;
這樣在表名稱比較長的時候可以非常方便的進行操作。
3、基本方法(步驟)
範例:查詢出以爲僱員的編號、姓名、職位、部門名稱、位置。
a) 確定所需要的數據儀表
|-emp表:可以查詢出僱員的編號、姓名、職位
|-dept表:可以查詢除部門的名稱和位置
b) 確定數據表的關聯字段:emp.deptno=dept.deptno ;
SELECT e.empno,e.ename,e.job,d.dname,d.loc
FROM emp e,dept d
WHERE e.deptno=d.deptno ;
範例:要求查詢出每一位僱員的姓名、職位、領導的姓名
A)確定所需要的表:
|-emp表(僱員):取得僱員的姓名、職位、領導編號;
|-emp表(領導):取得僱員的姓名(領導的姓名);
B)確定關聯字段:emp.mgr=memp.empno (僱員的領導編號=領導(僱員的)的僱員編號)
第一步:查詢每一位僱員的姓名和職位
SELECT e.ename,e.job
FROM emp e ;
第二步:查詢領導信息,加入自身關聯
SELECT e.ename,e.job,m.ename
FROM emp e,emp m
WHERE e.mgr=m.empno
範例:查詢出每個僱員的編號、姓名、基本工資、職位、領導的姓名部門的名稱及位置
SELECT e.empno,e.ename,e.sal,e.job,m.ename,d.dname LEADERNAME,d.loc LOCALTION
FROM emp e,emp m,dept d
WHERE d.deptno=e.deptno AND e.mgr=m.empno ;
範例:求出每一個僱員的編號、姓名、工資、部門名稱、工資等級
SELECT e.empno,e.ename,e.sal,d.dname,s.grade
FROM emp e,dept d,salgrade s
WHERE (e.deptno=d.deptno) AND (e.sal BETWEEN s.losal AND s.hisal) ;
4、左右連接
這是Oracle數據庫獨有的指的是查詢判斷條件的參考方向
a) (+)=:表示右鏈接
b) =(+):表示左連接
不用可以區分是左還是右,只需要根據查詢結果而定,如果發現有些需要的數據沒有顯示出來就使用此符號改變連接方向。
二、SQL:1999語法
除了上述的表連接操作之外,在SQL語法之中,也提供了一套用於表連接的操作SQL,格式如下:
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(table.column_name=table2.column_name)] ; |
以上實際上屬於多個語法的聯合,下面分塊說明語法的使用。
1、交叉連接(CROSS JOIN):用於產生笛卡爾積
SELECT * FROM emp CROSS JOIN dept ;
笛卡爾積本身並不是屬於無用的內容,在某些情況下還是需要應用的。
2、自然連接(NATURAL JOIN):自動找到匹配的關聯字段,消除笛卡爾積
SELECT * FROM emp NATURAL JOIN dept ;
但是並不是所有的字段都是關聯字段,設置關字段需要通過約定指定;
3、JOIN...USING子句:用戶指定一個消除笛卡爾積的關聯字段
SELECT * FROM emp JOIN dept USING(deptno) ;
4、JOIN...ON子句:用戶指定一個可以消除笛卡爾積的關聯條件
SELECT * FROM emp JOIN dept ON(emp.deptno=dept.deptno) ;
5、連接方向的改變:
|-左(外)連接:LEFT OUTER JOIN...ON ;
|-右(外)連接:ROGHT OUTER JOIN...ON ;
|-全(外)連接:FULL OUTER JOIN...ON ; —>把兩張表中沒有的數據都顯示
SELECT * FROM emp RIGHT OUTER JOIN dept ON(emp.deptno=dept.deptno) ;
*在Oracle之外的數據庫,都是用以上的SQL:1999語法。
三、統計函數及分組查詢(重點,難點)
1、統計函數
之前學過的一個COUNT()函數,實際上就是一個統計函數,常用的統計函數有:
|-COUNT():查詢表中的數據記錄;
|-AVG():求出平均值;
|-SUM():求和;
|-MAX():求出最大值 ;
|-MIN():求出最小值;
範例:測試COUNT()、AVG()、SUM(),統計出公司所有的僱員,每個月支付的工資和平均工資:
SELECT COUNT(*) 公司人數,SUM(sal) 每月支付工資總數,AVG(sal) 平均工資數 ;
注意:COUNT()函數主要功能是進行數據的統計,但是一張表內如果沒有記錄,COUNT()函數依然還會返回數據,只是返回的數據是0.
SELECT COUNT (enname) FROM BONUS ;
如果使用其他的函數,則有可能返回null,但COUNT()肯定會返回一個具體的數值。
2、分組統計
當數據有重複的時候,分組纔有意義。而如果想要分組,則需要使用GROUP BY子句來完成,此時的SQL語法如下:
SELECT [DISTNCT] *|分組字段1 [別名] [,分組字段2 [別名],...] | 統計函數
FROM 表名稱[別名][,表名稱[別名],……]
[WHERE 條件(s)]
[GROUP BY 分組字段1[,分組字段2,...]]
[ORDER BY字段[ASC | DESC]][,字段[ASC | DESC]];
範例:按照部門分組,求出每個部門的人數,平均工資
SELECT deptno,COUNT(deptno),AVG(sal)
FROM emp
GROUP BY deptno ;
範例:按照只爲分組求出每個職位的最高和最低工資。
SELECT job,COUNT(job),MIN(sal),MAX(sal)
FROM emp
GROUP BY job ;
注意:一旦分組之後對語法產生了新的限制,對於分組也有以下要求:
|-分組函數可以在沒有分組的時候單獨使用,可是不能出現其他的查詢字段;
|-如果現在要進行分組的話,則SELECT子句之後,只能出現分組的字段和統計函數,其他字段不能出現
|-分組函數允許嵌套,但是嵌套之後的分組函數的查詢之中,不能再出現任何其他字段。
範例:查詢出每個部門的名稱、部門的人數、平均工資。
l 確定所需要的數據表:
|-dept表:每個部門的名稱、位置;
|-emp表:統計出部門的人數、平均工資 ;
l 確定已知的關聯字段:emp.deptno=dept.deptno
l 將兩張表關聯起來
SELECT d.dname,e.empno,e.sal
FROM dept d,emp e
WHERE d.deptno=e.deptno (+) ;
此時的查詢結中也是存在這數據的重複,以往都是在實體表中直接進行分組,現在上圖中的表爲返回結果的虛擬表。但是這都不影響分組操作。只要數據存在這重複就可以進行分組。
到這裏的操作都是針對於單字段分組的,而實際情況下也可以實現多字段的分組。
範例:查詢出每個部門的名稱、部門的人數、平均工資。
l 確定所需要的數據表:
|-dept表:每個部門的名稱、位置;
|-emp表:統計出部門的人數、平均工資 ;
l 確定已知的關聯字段:emp.deptno=dept.deptno
l 將兩張表關聯起來
此時的返回結果上依然後大量的重複數據,而且平均的分在了三列之上(deptno,dname,loc),那麼就可以在分組的字段中寫上3個字段。
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),NVL(AVG(e.sal),0)
FROM dept d,emp e
WHERE d.deptno=e.deptno (+)
GROUP BY d.deptno,d.dname,d.loc;
以上就是多字段分組操作,但是不管是單字段還是多字段,一定都要有一個前提,那就是重複數據!
3、WHERE子句中不能使用統計函數
範例:統計出每個部門的詳細信息,並要求這些部門的平均工資高於2000;
在以上程序的基礎上完成改良,在之前唯一學習的限定查詢語法只有WHERE語法,所以下面用WHERE完成要求。
SELECT d.deptno,d.dname,d.loc,COUNT(empno),AVG(sal+NVL(comm,0))
FROM emp e,dept d
WHERE d.deptno=e.deptno(+) AND AVG(e.sal)>2000
GROUP BY d.deptno,d.dname,d.loc ;
執行這個語句時會報錯:
錯誤提示的意思就是不能在WHERE語句之中使用統計函數。
WHERE是在GROUP語句之前執行,而GROUP又是在所有數據之中進行處理,所以WHERE語句就沒用了,所以會報錯。
這是就可以使用HAVING子句來實現。
注意:WHERE和HAVING之間的區別:
l WHERE:是在執行GROUP BY 操作之前進行過濾,表示從全部數據之中篩選出部分數據,在WHERE子句中不能使用統計函數;
l HAVING:是在GROUP BY 子句操作之後進行再次過濾,可以在HAVING子句之中使用統計函數。
範例:顯示非銷售人員的工作名稱以及從事同一工作的僱員的月工資的總和,並且要滿足從事同一工作的僱員的月工資合計大於¥5000,輸出結果按月工資的合計升序排列:
分析:
第一步:查詢出所有的非銷售人員的信息;
第二步:按照職位分組,並且用SUM()函數統計月工資總和;
第三步:月工資的合計是通過統計函數查詢的,所以現在這個對分組之後的過濾要使用HAVING子句完成。
第四步:按照升序排序
SELECT job,SUM(sal) sum
FROM emp
WHERE job!='SALESMAN'
GROUP BY job
HAVING SUM(sal)>5000
ORDER BY sum ;
以上題目融合了分組操作大部分的語法使用。
四、子查詢(核心)
子查詢=簡單查詢+限定查詢+多表查詢+統計查詢的綜合體
多表查詢並不被建議使用,多表查詢的最有力的替代者 就是子查詢,所以子查詢在開發中使用的相當的多;
所謂的子查詢就是在一個查詢中嵌套了多個其他若干查詢,語法如下:
SELECT [DISTNCT] *|分組字段1 [別名] [,分組字段2 [別名],...] | 統計函數(
SELECT [DISTNCT] *|分組字段1 [別名] [,分組字段2 [別名],...] | 統計函數
FROM 表名稱[別名][,表名稱[別名],……]
[WHERE 條件(s)]
[GROUP BY 分組字段1[,分組字段2,...]]
[ORDER BY字段[ASC | DESC]][,字段[ASC | DESC]];)
FROM 表名稱[別名][,表名稱[別名],……](
SELECT [DISTNCT] *|分組字段1 [別名] [,分組字段2 [別名],...] | 統計函數
FROM 表名稱[別名][,表名稱[別名],……]
[WHERE 條件(s)]
[GROUP BY 分組字段1[,分組字段2,...]]
[ORDER BY字段[ASC | DESC]][,字段[ASC | DESC]];)
[WHERE 條件(s)] (
SELECT [DISTNCT] *|分組字段1 [別名] [,分組字段2 [別名],...] | 統計函數
FROM 表名稱[別名][,表名稱[別名],……]
[WHERE 條件(s)]
[GROUP BY 分組字段1[,分組字段2,...]]
[ORDER BY字段[ASC | DESC]][,字段[ASC | DESC]];)
[GROUP BY 分組字段1[,分組字段2,...]]
[ORDER BY字段[ASC | DESC]][,字段[ASC | DESC]];
理論上子查詢可以出現在查詢語句的任何地方,一般情況下子查詢出現在WHERE和FROM語句之後比較多。
個人經驗:(如果子查詢出現在下面的子句之後)
l WHERE:子查詢一般只返回單行單列、多行單列、單行多列的數據
l FROM:子查詢一般返回多行多列數據,作爲臨時表出現
範例:要求查詢工資比SMITH高的所有員工的全部信息。
第一步:查詢SMITH的工資:
SELECT sal FROM emp WHERE ename=’SMITH’ ;
第二步:因爲返回的數據是單行單列,所以將數據插入WHERE子句之後:
SELECT * FROM emp
WHERE sal>(
SELECT sal
FROM emp
WHERE ename=’SMITH’);
範例:查詢超出公司平均工資的全部僱員信息
第一步:查詢公司的平均工資
第二步:由於是返回數據是單行單列所以子查詢跟在WHERE子句後面
SELECT * FROM emp
WHERE sal>(
SELECT AVG(sal)
FROM emp );
以上範例均爲返回單行單列數據,也可以返回單行多列(很少見)、多行單利或多行多列的數據,下見範例:
SELECT * FROM emp
WHERE (ename,job)=(
SELECT ename,job
FROM emp
WHERE ename=’ALLEN’) ;
返回數據是單行多列的情況並不多見,但是返回數據是多行單列很是常見,這時需要3種判斷符:IN、ANY、ALL
1、判斷符:用於指定一個子查詢的判斷範圍
這個判斷符的使用實際上和之前的IN操作是一樣的,但是範圍由子查詢指定了
SELECT * FROM emp
WHERE sal IN(
SELECT sal
FROM emp
WHERE job=’MANAGER’) ;
注意:在使用IN的時候注意NOT IN的問題,在使用NOT IN的子查詢之中,如果有有一個內容是null值的話,則不會查詢出任何結果。因爲NOT IN表示查詢所有,爲了防止此類安全漏洞Oracle設計爲完全不查詢/顯示數據。
2、ANY操作符:與每一個內容相匹配,有三種匹配方式
l =ANY:功能和IN操作符完全一致
SELECT * FROM emp
WHERE sal =ANY(
SELECT sal
FROM emp
WHERE job='MANAGER') ;
l >ANY:比子查詢中返回記錄最小的還要大的數據(不包含);
SELECT * FROM emp
WHERE sal >ANY(
SELECT sal
FROM emp
WHERE job='MANAGER') ;
l <ANY:比子查詢中返回記錄最大的還要小的數據(不包含);
SELECT * FROM emp
WHERE sal <ANY(
SELECT sal
FROM emp
WHERE job='MANAGER') ;
3、ALL操作符:與每一個內容相匹配,有兩種形式:
l >ALL:比子查詢中返回的最大的記錄還要大
SELECT * FROM emp
WHERE sal >ALL(
SELECT sal
FROM emp
WHERE job='MANAGER') ;
l <ALL:比子查詢返回的最小數據還要小
SELECT * FROM emp
WHERE sal <ANY(
SELECT sal
FROM emp
WHERE job='MANAGER') ;
以上所有的子查詢都是在WHERE子句中出現的,那麼下面來觀察FROM子句中出現的子查詢,在這個子查詢一般都返回的是多行多列的數據,但作是一張臨時表來處理。
範例:查詢出每個部門的名稱、位置、編號、部門人數、平均工資
回顧:以前採用的是多字段分組統計
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno) COUNT,NVL(AVG(e.sal),0) AVGSAL
FROM emp e,dept d
WHERE d.deptno=e.deptno(+)
GROUP BY d.deptno,d.dname,d.loc
ORDER BY d.deptno;
這個時候實際上產生了笛卡爾積,產生了56條記錄,下面是新的解決方案:子查詢。所有的子查詢只能在GROUP BY中,所以在子查詢中負責統計數據,而在外部數據之中負責將統計數據和dept表數據相統一。
SELECT d.deptno,d.dname,d.loc,temp.count,temp.avg
FROM dept d,(
SELECT deptno,COUNT(empno) count,AVG(sal) avg
FROM emp
GROUP BY deptno) temp
WHERE d.deptno=temp.deptno(+) ;
現在的程序中所操作的數據量:
|-子查詢中的統計記錄是14條,最終統計的顯示記錄是3條;
|-dept表之中一共有4條記錄;
|-如果現在產生的笛卡爾積的話只有12條記錄,再加上僱員的14條記錄一共才26條記錄
綜上,子查詢的性能的確優於多表查詢。
經驗:大部分情況之下,如果最終的查詢結果之中需要出現SELECT子句,但是又不能直接使用函數的時候就在子查詢中統計信息,即:有複雜統計的地方大部分都需要子查詢。
五、數據庫的更新操作
DML語法操作之中,除了查詢之外還有數據庫的更新操作,數據的更新操作主要是指:添加、修改、刪除數據。但是考慮到emp表以後還要繼續使用,所以下面現將emp表複製一份,輸入如下指令:
CREATE TABLE myemp AS SELECT * FROM emp ;
這種語法是Oracle中支持的操作,其他的數據庫不一樣。
一、數據增加
如果現在想要實現數據庫的增加操作,則可以使用如下的語法完成:
INSERT INTO 表名稱[(字段1,字段2,...)] VALUES(值1,值2,...) ;
如果想要進行增加數據的話,一下集中數據類型要分別處理:
|-增加的是數字:直接編寫數字,例如:123 ;
|-增加字符串:字符串:字符串應該使用’’(單引號)進行申明;
|-增加DATE數據:
|-第一種:可以按照已有的字符串的格式編寫字符串,例如“17-12 月-80”
|-第二種:利用TO_DATE()函數將字符串變爲DATE型數據;
|-第三種:榮國設置的時間如當前系統時間,則使用SYSDATE;
可是對於數據的增加也有兩種操作格式:完整型、簡便型;
範例:增加數據——完整語法編寫
INSERT INTO myemp (empno,ename,hiredate,sal,mgr,job,comm)
VALUES(8888,’張三’,TODATE(‘1960-08-17’,’yyyy-mm-dd’),8000,7369,’清潔工’,1000)
INSERT INTO myemp (empno,ename,hiredate,sal,mgr,job,deptno)
VALUES(8888,'李四',SYSDATE,3000,7369,'clearer',30) ;
範例:增加數據——簡便即不寫增加的數據的列名稱
雖然這種方法看似節省了代碼,但在實際的開發中,這樣操作很不利於程序的維護。因爲要求添加的數據必須如原表的數據在順序、類型、數量上完全一致才行。
二、數據修改
如果是想修改已有的數據,則可以按照如下的語法進行:
UPDATE 表名稱 SET 更新字段1=更新值1,更新字段2=更新值2……[WHERE 更新條件(s)]
範例:更新僱員編號是7369的基本工資爲5000,獎金是2000,職位是manager,僱傭日期改爲今天;
UPDATE myemp SET sal=5000,comm=2000,job=’MANAGER’.hiredate=SYSDATE WHERE empno=7369 ;
範例:所有人的工資上漲50%
UPDATE myemp SET sal=sal*1.5 ;
在更新時,如果不添加WHERE條件的話便會將對錶中的所有記錄進行更新。這樣做是相當不好及危險的。當表中的數據量較大時(3000-5000時明顯表現出來),速度和性能會明顯的下降。
三、刪除數據
當某些數據不需要之後,便可以DELETE語句將其刪除,語法如下:
DELETE FROM 表名稱 [WHERE 刪除條件(s)] ;
與更新一樣,如果未添加刪除條件的話,便會刪除所有記錄。
範例:刪除所有在1987年僱傭的僱員
DELETE FROM myemp WHERE TO_CAHR(hiredate,’yyyy’)=1987 ;
一定記住,如果刪除的時候沒有匹配的條件的數據存在,則更新數據量爲’0條’,更新操作也是如此;
範例:刪除表中的全部記錄
DELETE FROM myemp ;
一般對於刪除操作而言,儘可能的減少去使用,包括以後進行系統開發的時候,對於所有的刪除操作也建議大家給出一個確認的提示框,以防止用戶誤刪。
六、事務處理
對於數據表的操作,很明顯查詢要比操作更加的安全,因爲更新操作有可能會出現錯誤,以導致沒有按照既定的要求正確的完成我們的更新操作。
但是在很多時候,更新會由多條指令共同完成,例如:銀行的轉賬方法
|-判斷A的賬戶上是否有5000w
|-判斷B賬戶是否存在並且狀態是否正常
|-從A的賬戶上移走5000w
|-從B的賬戶上增加5000w
|-向銀行支付手續費用5000;
以上的五個數據操作是一個整體,可以理解爲一個完整的業務,如果
這裏面的第三點出錯了,所有的其他操作都不應該執行,並且迴歸到最初的狀態
所有的事務處理操作都是針對每一個session進行的,在Oracle數據庫中把每一個連接到數據庫上的用戶都稱爲一個session,每一個session之間彼、此獨立,不會進行任何通訊而且每一個session獨享自己的事務控制。而事務控制之中主要使用兩個命令:
l 事務的回滾:ROLLBACK更新操作回到原點
l 事務的提交:COMMIT,真正的發出更新操作,一旦提交就無法回滾
但是這樣也會出現一些問題,例如:
某一個session在更新數據庫之前還沒有提交事務,其他session是無法更新的,必須等到之前的session提交之後纔可以;