Oracle的知識點總結
語法部分主要補充與MySQL不同的地方
1. 連接符||
--需求1:查詢出員工的名字,要求顯示的員工名字前面加上“姓名:”的字符串,顯示結果參考:姓名:scott
SELECT '姓名:'||ename FROM emp;
--需求2:將和員工的編號和員工的姓名都放在一個結果字段中顯示。
SELECT empno||'>>'||ename FROM emp;
注意:單引號表示字符串,雙引號用于別名
2. 偽表-dual
DUAL 是一個‘偽表’(也稱之為萬能表),可以用來測試函數和表達式
--查詢顯示當前日期
SELECT SYSDATE FROM dual;
SELECT 1+1 FROM dual;
3.過濾語句查詢where
--需求1:查詢關于KING這個人的記錄。
SELECT * FROM emp WHERE ename = 'KING';
--需求2:查詢入職日期是1987/4/19的員工的信息。
SELECT * FROM emp WHERE hiredate = to_date('1987/4/19','yyyy/MM/dd');
4. 轉義字符-Escape
--需求1:查詢名稱是帶有”x”字符的員工的記錄信息。
SELECT * FROM emp WHERE ename LIKE '%x%';
--需求2:查詢員工名稱中含有下劃線(“_”)的員工.
SELECT * FROM emp WHERE ename LIKE '%|_%' ESCAPE '|';
--需求3:查詢姓名是4個字符的員工的信息。
SELECT * FROM emp WHERE ename LIKE '____';
escape用于轉義特殊字符.在需要轉義的地方前加上escape規定的轉義符
5. 單行函數
5.1 字符函數
1. 字符函數
1. 大寫 upper
2. 小寫 lower
3. 首字母大寫 initcap
select upper('hello') from dual;
select lower('HELLO') from dual;
select initcap('i love you') from dual;
2. 字符控制函數
1. concate / || 連接
2. substr 截取
3. length 長度
4. instr 第幾個
5. trim 去除首位空格/指定字符
6. replace 替換
select concat('hello',' world') from dual;--hello world
select substr('helloworld',3,6) from dual;--llowor,第幾個開始截,包頭不包尾
select length('hello') from dual;--5
select instr('hello','h') from dual;--1,第幾個
select trim('H' from 'Hello WorHldH') from dual;--ello WorHld去掉首尾H
select trim(' h h hhhh ') from dual;--h h hhhh去掉首尾空格
select replace('java','a','e') from dual;--jeve
5.2 數字函數
round 四舍五入
trunc 截斷
-
mod 求余
select round(13.16,1) from dual;--13.2 select trunc(13.16,1) from dual;--13.1 select mod(20,3) from dual;--2
5.3 日期函數
需求1:計算員工的工齡(工齡:當前的日期和入職的日期的差),要求分別顯示員工入職的天數、多少月、多少年。
select round(sysdate-hiredate)||'天' 入職天數,trunc(months_between(sysdate,hiredate),1)||'月' 入職月數,
trunc(months_between(sysdate,hiredate)/12,1)||'年' 入職年數 from emp;
需求2:查看當月最后一天的日期。
select to_char(last_day(sysdate),'yyyy-MM-dd') from dual;--2017-07-31,sql語句不區分大小寫,MM是因為習慣
需求3:查看指定日期的下一個星期天或星期一的日期。(next_day(基礎日期,星期幾))
select to_char(next_day(sysdate,1),'yyyy-MM-dd') from dual;--下個星期天的日期,2017-07-23
select to_char(next_day(sysdate,2),'yyyy-MM-dd') from dual--下個星期天的日期,2017-07-24
//超過12點返回第二天的日期,否則返回當天的日期
select round(sysdate) from dual;
5.4 轉換函數
to_char : 轉換為字符格式
to_date : 轉換為日期格式
to_number : 轉換為數字格式
需求1:顯示今天的完整日期,結果參考:“2015-07-06 11:07:25”。
select to_char(sysdate,'yyyy-MM-dd hh24:mi:ss') from dual;--2017-07-17 21:05:21
需求2:顯示今天是幾號,不包含年月和時間,結果參考:“8日”。
select to_char(sysdate,'dd')||'日' from dual;--8日
需求3:顯示當月最后一天是幾號,結果參考:”30“。
select to_char(last_day(sysdate),'dd') from dual;--31
需求4:xiaoming的入職日期是2015-03-15,由于其入職日期當時忘記錄入,現在請將其插入到emp表中。
update emp set hiredate=to_date('2015-03-15','yyyy-MM-dd') where ename ='xiao_ming';
需求5:查看2015年2月份最后一天是幾號,結果參考“28“
select to_char(last_day(to_date('2015-02','yyyy-MM')),'dd') from dual;--28
---------------------------------------------------------
和java不同,Oracle的日期格式對大小寫不敏感。
日期格式的常見元素:
--需求:查看顯示今天是星期幾
select to_char(sysdate,'day') from dual;--monday
數字格式的常見元素:
9代表任意數字,可以不存在。0代表數字,如果該位置不存在,則用0占位。
需求:查詢員工的薪水,格式要求:兩位小數,千位數分割,本地貨幣代碼。
select to_char(sal,'L99990.00') from emp;--$1600.00
5.5 濾空函數
1. nvl(a,c),當a為null的時候,返回c,否則,返回a本身。
2. nvl2(a,b,c),當a為null的時候,返回c,否則返回b
其中,nvl2中的2是增強的意思,類似于varchar2。
3. nullif(a,b),當a=b的時候,返回null,否則返回a
4. coalesce(a,b,c,d),從左往右查找,當找到第一個不為null的值的時候,就顯示這第一個有值的值。
--需求:查詢員工的月收入(基本薪資+獎金)
select ename 員工姓名,sal+nvl(comm,0) from emp where sal is not null;
SELECT coalesce(NULL,NULL,1,2) FROM dual;--1,返回第一個不為空的值
5.6 條件表達式
- CASE 表達式
表達式1:
case job
when 條件 then 結果
when 條件 then 結果
...
else 結果
end
表達式2:
case
when job=條件 then 結果
when job=條件 then 結果
......
else 結果
end
- DECODE 函數
decode(job,條件1,結果1,條件2,結果2...,'其他結果')
--需求:要將工種job轉換為中文
select ename,case
when job='CLERK' then '辦事員'
when job='SALESMAN' then '銷售人員'
else '閑雜人等'
end
from emp;
select ename,decode(job,'CLERK','辦事員','SALESMAN','銷售人員','閑雜人等') from emp;
decode只能用于等于的情況,如果要比較大小進行條件判斷,就只能用case表達式。
-------------------------------------------------------
需求:查看公司員工的工資情況,要求顯示員工的姓名、職位、工資、以及工資情況。
如果是工資小于1000,則顯示“工資過低”,工資大于1000小于5000為“工資適中”,工資大于5000的,則顯示“工資過高”:
select ename,case
when sal<1000 then '工資過低'
when sal>=1000 and sal<5000 then '工資適中'
when sal>=5000 then '工資過高'
else '工資未知'
end "薪資等級"
from emp;
6. 多行函數
多行函數也稱之為分組函數、聚集函數。
就是把多行的值匯聚計算成一個值。
count,max,min,avg,sum :多行函數會自動濾空。
count(主鍵)效率很高。主鍵自動會有索引(提升查詢效率的),這個效率最高。
count(*)全表字段掃描,效率低,但現在的數據庫都對此做了優化,底層根據主鍵
count(1)效率高,如果沒有索引,這個效率比較高
--需求:查詢所屬部門號大于等于20的員工信息。(無法使用having子句)
select * from emp where deptno >= 20;
--需求:查詢平均工資大于2000的部門信息,要求顯示部門號和平均工資
select deptno,avg(sal) from emp group by deptno having avg(sal)>=2000;
--需求1:查詢顯示各個部門的平均薪資情況,并且按照部門號從低到高排列。
select d.deptno,d.dname,t.s from dept d,(select deptno,avg(sal) s from emp group by deptno) t where d.deptno=t.deptno order by deptno;
--需求2:查詢顯示各個部門的不同工種的平均薪資情況,并且按照部門號從低到高排列。
select deptno,job,avg(sal) from emp group by job,deptno order by deptno;
sql語句優化:加上前綴效率高。
7. 多表查詢
7.1 內連接
--需求:查詢一下員工信息,并且顯示其部門名稱
select e.ename,d.dname from emp e,dept d where e.deptno=d.deptno;
顯式內連接:
select e.ename,d.dname from emp e inner join dept d on e.deptno=d.deptno;
7.2 左外連接
--查詢"所有"員工信息,要求顯示員工號,姓名 ,和部門名稱--要求使用左外連接
select e.empno,e.ename,d.dname from emp e left join dept d on e.deptno=d.deptno;
--Oracle特有語法:把+號看成擴展補充數據就就容易理解了
select e.empno,e.ename,d.dname from emp e,dept d where e.deptno=d.deptno(+);
7.3 右外連接
--查詢“所有”部門及其下屬的員工的信息。--右外連接
select e.empno,e.ename,d.dname from emp e right join dept d on e.deptno=d.deptno;
--Oracle特有語法(+)
select e.empno,e.ename,d.dname from emp e,dept d where e.deptno(+)=d.deptno;
7.4 練習
--1.查詢員工信息,要求同時顯示員工和員工的領導的姓名
select t1.empno,t1.ename,t2.ename from emp t1,emp t2 where t1.mgr=t2.empno;
select e.empno,e.ename,e.mgr,t2.ename from emp e,(select t.empno,t.ename from emp t) t2 where e.mgr=t2.empno;
--2.查詢“所有”員工信息,要求同時顯示員工和員工的領導的姓名(所有--左外連接)
select t1.empno,t1.ename,t2.ename from emp t1 left join emp t2 on t1.mgr=t2.empno;
--需求:查找工作和'SMITH' 'ALLEN' 這兩個人的工作一樣的員工信息
select * from emp where job in (select job from emp t2 where t2.ename='SMITH' or t2.ename='ALLEN');
--需求:查找工作和'SMITH' 'ALLEN' 這兩個人的工作不一樣的員工信息
select * from emp where job not in(select job from emp t2 where t2.ename='SMITH' or t2.ename='ALLEN');
--需求:查詢工資比30號部門任意一個員工的工資高的員工信息。
select * from emp where sal>(select min(sal) from emp where deptno=30);
--需求:查詢工資比30號部門所有員工的工資高的員工信息。
select * from emp where sal>(select max(sal) from emp where deptno=30);
8. 偽列
rownum、rowid
8.1 rownum 行號
查詢操作時由ORACLE為每一行記錄自動生成的一個編號
行號排序:
--需求:查詢出所有員工信息,按部門號正序排列,并且顯示默認的行號列信息。
select rownum,t.* from (select * from emp order by deptno) t;
order by排序,不會影響到rownum的順序。rownum永遠按照默認的順序生成。
所謂的“默認的順序”,是指系統按照記錄插入時的順序(其實是rowid)。
行號分頁:
--需求:根據行號查詢出第四條到第六條的員工信息。
--先子查詢出小于第六條記錄的所有員工信息(盡量讓虛表盡量小)
select * from (select rownum r,t.* from emp t where rownum<=6) t2 where r>=4;
--需求:要分頁查詢,每頁3條記錄,查詢第二頁
/*
pageSize=3,pnum=2
beginRownum=(pnum-1)*pageSize+1;--4
endRownum=pageSize*pnum;--6
*/
--寫Oracle的分頁,從子查詢寫起,也就是說從小于等于寫起,或者說從endRownum寫起
--select rownum r,t.* from emp t where rownum<=endRownum;
select rownum,t.* from emp t where rownum<=6;--使虛表盡量小
--將上面查詢出來帶有行號的表作為虛表查詢,這時rownum代表的列就代表真實的列了
select * from (select rownum r,t.* from emp t where rownum<=6) t2 where t2.r>=4;
--按照薪資的高低排序再分頁
--1. 先排序
select * from emp order by sal desc;
--2.將排序后的表作為一張虛表,加上虛擬行號,將查詢出來的表再作為虛表
select rownum r,t1.* from (select * from emp order by sal desc) t1 where rownum<=6;
--3.最后的分頁
select * from (select rownum r,t1.* from (select * from emp order by sal desc) t1 where rownum<=6)
where r>=4;
--通用
SELECT * FROM
(
SELECT ROWNUM r,t.* FROM
(SELECT ename,job,sal FROM emp ORDER BY sal DESC) t
WHERE ROWNUM <=endRownum ORDER BY sal DESC
)
WHERE r >=firstRownum ;
8.2 rowid 記錄編號
ROWID(記錄編號):是表的偽列,是用來唯一標識表中的一條記錄,并且間接給出了表行的物理位置,定位表行最快的方式。
使用insert語句插入數據時,oracle會自動生成rowid并將其值與表數據一起存放到表行中。
--需求:刪除表中的重復數據,要求保留重復記錄中最早插入的那條。
--根據姓名分類,找出相同分類中最小的rowid
select min(rowid) from test01 group by name;
--刪除除了同一分類中除最小rowid的其他記錄
delete from test01 where rowid not in(select min(rowid) from test01 group by name);
刪除重復記錄一定要小心,如果條件有問題,就會刪錯數據.刪除之前,先用查詢查一下,看是否是目標數據。
rowid與rownum不同,它是在數據插入表格中時一起存入到表格中,只是我們平時不可見,因此在查詢時,不需要將它跟表格的其他數據查詢出來作為虛表,直接使用即可.而rownum只是查詢時才生成的,因此利用它分頁時必須將rownum與其他查詢出來的數據作為虛表進行進一步的查詢
9. 增刪改
9.1 Insert
單條插入:
insert into table values(...);
--批量插入語法(主要用于將一張表中的數據批量插入到另外一張表中)
insert into test01 select empno,ename from emp;
--拷貝表
create table test02 as select * from emp;
--拷貝表結構
create table test01 as select * from emp where 1=2;
9.2 Delete和truncate區別
- delete逐條刪除,truncate先摧毀表,再重建 。
- 最根本的區別是:delete是DML(可以回滾,還能閃回),truncate是DDL(不可以回滾)
- delete不會釋放空間,truncate會(當確定一張表的數據不再使用,應該使用truncate)
- delete會產生碎片,truncate不會。
Hwm-高水位
高水位線英文全稱為high water mark,簡稱HWM,那什么是高水位呢 ?
在Oracle數據的存儲中,可以把存儲空間想象為一個水庫,數據想象為水庫中的水。
水庫中的水的位置有一條線叫做水位線,在Oracle中,這條線被稱為高水位線(High-warter mark, HWM)。
在數據庫表剛建立的時候,由于沒有任何數據,所以這個時候水位線是空的,也就是說HWM為最低值。
當插入了數據以后,高水位線就會上漲,但是這里也有一個特性,就是如果你采用delete語句刪除數據的話,數據雖然被刪除了,
但是高水位線卻沒有降低,還是你剛才刪除數據以前那么高的水位。也就是說,這條高水位線在日常的增刪操作中只會上漲,不會下跌。
高水位對查詢有巨大的影響。而且還浪費空間。
如何解決高水位帶來的查詢效率問題?
- 1.將表數據備份出來,摧毀表再重建(truncate table),然后再將數據導回來。
- 收縮表,整理碎片,可使用變更表的語句:alter table 表名 move
查看、測試、消除高水位—了解
--之前查看rowid
SELECT t.*,ROWID FROM TEST t;
--對表進行分析,收集統計信息(執行了收集信息的動作,user_tables表的塊字段才有數據)
analyze table TEST compute statistics;
--查詢表數據的塊信息,其中blocks是高水位,empty_blocks是預申請的塊空間。
select table_name,blocks,empty_blocks from user_tables where table_name='TEST';
--收縮表(整理碎片),降低高水位,消除行移植和行鏈接,不釋放申請的空間
ALTER TABLE TEST MOVE;
--對表進行分析,收集統計信息(執行了收集信息的動作,user_tables表的塊字段才有數據)
analyze table TEST compute statistics;
--查詢表數據的塊信息,其中blocks是高水位,empty_blocks是預申請的塊空間。
select table_name,blocks,empty_blocks from user_tables where table_name='TEST';
--之后查看rowid
SELECT t.*,ROWID FROM TEST t;
- 收縮表之后,高水位線下降了。
- 收縮表之后,rowid發生了變化。
注意:
- move最好是在空閑時做,記得move的是會產生鎖的(如果你move的時候需要很長事件,那么別人是不能操作這張表的。排他鎖)
- move以后記得重建index(索引存放的其實就是數據的地址信息。當數據的地址變動了,索引也會失效。)語法:ALTER INDEX 索引名字 REBUILD;
10. 對表的操作
--增加字段
alter table test01 add address varchar2(60);
--刪除字段
alter table test01 drop column address;
--修改字段type
alter table test01 modify name varchar2(100);
--修改字段名
alter table test01 rename column name to ename;
--修改表名(執行RENAME語句改變表, 視圖, 序列, 或同義詞的名稱。)
rename test01 to test1;
--刪除表
drop table test02;
11. 序列-sequence
Mysql中主鍵有自增長的特性.
Oracle中,主鍵沒有自增長這個特性.
使用序列高效的生成主鍵值。
11.1 創建序列
在ORACLE中為序列提供了兩個偽列:
1,NEXTVAL 獲取序列對象的下一個值(指針向前移動一個,并且獲取到當前的值。)
2,CURRVAL 獲取序列對象當前的值
--創建一個簡單的序列
create sequence seq_test;
--使用序列,取出當前序列,指針跳到下一個
select seq_test.nextval from dual;
--取出當前序列,指針不變動
select seq_test.currval from dual;
11.2 序列的應用
在插入數據的時候插入序列主鍵.
insert into test1 values(seq_test.nextval,'tomcat'||seq_test.nextval);
11.3 序列的裂縫
- 序列是一個共有對象,多個表都可以調用。
- 當插入記錄時報錯,序列對象值也被使用,下一次再使用時,序列的值就會+1
也就是說,用序列插入數據庫的值不一定是連續的。
序列出現裂縫的條件:
- 事務回滾。
- 系統異常。
- 多個表同時使用同一個序列。
12. 表空間tablespace
常見表空間的分類:
- (永久)數據表空間,主要用來永久存儲正式的數據文件。
- 臨時數據表空間,主要用來存儲臨時數據的,比如數據的排序、分組等產生的臨時數據,不能存放永久性對象。
- UNDO表空間,保存數據修改前的鏡象
臨時表空間和UNDO表空間的異同:
相同之處:兩者都不會永久保存數據。
不同之處:
UNDO表空間用于存放UNDO數據,當執行DML操作時,oracle會將這些操作的舊數據寫入到UNDO段,以保證可以回滾和事務隔離讀取等,主要用于數據的修改等;
而臨時表空間主要用來做查詢和存放一些緩沖區數據。
12.1 創建表空間
表空間的創建一般是由DBA來操作完成的,而且需要管理員權限
三種表空間中,UNDO表空間通常是由Oracle自動化管理的,而另外兩種表空間則一般需要手動創建。
永久表空間語法:
create tablespace tablespace_name
[datafile datafile1,[datafile 2]…]
[logging | nologging]
[online|offline]
[extent_management_clause]
參數:
* tablespace_name:表空間名稱隨意,但最好遵循一定的規范,如tbl_itdream_dat、tbl_itdream_tmp等。
* datafile : 表空間的類型
* datafile1 : 數據文件需要有如下格式:文件名 SIZE 初始文件大小 [autoextend off| on] [maxsize|next size maxSize size]
* 文件名是數據文件的路徑名,可以是絕對路徑,也可以是相對路徑,如“路徑\xxx.dbf”,注意路徑必須先建立好。
* 初始化文件大小,是數據文件剛建立起來的時候所占物理磁盤空間的大小
* autoextend,是否自動擴展數據文件的大小,OFF表示關閉自動擴展,數據文件只能是初始大小,ON表示開啟自動擴展,當數據文件超過初始大小的時候,會自動增大。默認值為OFF。
* 如果設置自動擴展,則需要設置最大值MAXSIZE,如設置2000m,當然也可以設置為UNLIMITED,表示無限表空間。
如果要指定每次擴展的大小,可以使用NEXT SIZE MAXSIZE SIZE語法,表示每次擴展多少尺寸,最大能擴展到多大(大小上限)。
* [logging | nologging]該子句用來聲明這個表空間上所有的用戶對象的日志屬性,默認為logging
* [online|offline]表空間的狀態,online表示表空間創建后立即有效,offline表示表空間創建后暫時無效,即不能使用,只有設置為online后才有效,默認值為online。
* extent_management_clause表空間如何管理范圍,推薦設置為本地管理,值為extent management local.生產環境推薦本地管理。
臨時表空間語法:
--創建臨時數據表空間
create temporary tablespace tablespace_name
tempfile datafile1,[datafile 2]…
extent_management_clause
臨時數據表空間的數據文件一般不需要指定最大值,Oracle對其采用了貪吃算法策略,因此,該表空間會自動逐漸增大。
其他參數見永久表空間。
臨時表空間默認是不記日志的
創建表空間:
最簡語法:
--永久數據表空間和臨時數據表空間的建立。
create tablespace tbl_test1_dat
datafile
'c:/tbl_test1_dat01.dbf' size 100M
autoextend on next 5M maxsize 2000M
extent management local;
--創建臨時數據表空間。
create temporary tablespace tbl_test2_tmp
tempfile
'c:/tbl_test1_tmp01.dbf' size 20M
extent management local;
注意:實際開發中,不要用最簡化的方式來進行表空間的創建。
參考1:
--創建數據表空間
CREATE TABLESPACE TBS_CSP_BS_DAT
DATAFILE '/dev/rlv_dat001' SIZE 2000M REUSE AUTOEXTEND OFF,
'/dev/rlv_dat002' SIZE 2000M REUSE AUTOEXTEND OFF
LOGGING
ONLINE
PERMANENT
EXTENT MANAGEMENT LOCAL;
--創建臨時數據表空間
CREATE TEMPORARY TABLESPACE TBS_CSP_BS_TMP
TEMPFILE '/dev/rlv_dat009' SIZE 2000M REUSE AUTOEXTEND OFF
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 10M;
注:PERMANENT是顯式的指定創建的是永久的表空間,用來存放永久對象。默認值。
參考2:
--創建數據表空間
create tablespace tbs_user_data
logging
datafile 'D:\oracle\oradata\Oracle9i\user_data.dbf'
size 50m
autoextend on
next 50m maxsize 20480m
extent management local;
--創建臨時數據表空間
create temporary tbs_user_temp
tempfile 'D:\oracle\oradata\Oracle9i\user_temp.dbf'
size 50m
autoextend on
next 50m maxsize 20480m
extent management local;
12.2 刪除表空間
drop tablespace 表空間名 [including contents and datafile]
----刪除表空間以及下面所有數據和數據文件(全刪,寸草不生)
drop tablespace tbl_test2_tmp including contents and datafiles;
如果不加后面的including...,則只是將表空間進行了邏輯刪除(Oracle無法管理使用這個表空間了,但數據文件還存在)。
13. 用戶與權限
SYS 帳戶(數據庫擁有者):
擁有 DBA 角色權限
擁有 ADMIN OPTION 的所有權限
擁有 startup, shutdown, 以及若干維護命令
擁有數據字典
system賬戶:
擁有 DBA 角色權限.
Sys和system賬戶的區別:
sys用戶是數據庫的擁有者,是系統內置的、權限最大的超級管理員帳號。
system用戶只是擁有DBA角色權限的一個管理員帳號,其實它還是歸屬于普通用戶。
13.1 創建用戶(在dba權限下)
* 創建用戶的語句
create user 用戶名
identified by 密碼(不要加引號)
default tablespace 默認表空間名 quota 5M on 默認表空間名
[temporary tablespace 臨時表空間名]
[profile 配置文件名] //配置文件
[default role 角色名] //默認角色
[password expire] //密碼失效
//如果設置失效,那么第一次登錄的時候,會提醒你更改密碼。
[account lock] //賬號鎖定(停用)
* 修改用戶
alter user 用戶名 identified by 密碼 quota 10M on 表空間名
alter user 用戶名 account lock/unlock
* 刪除用戶
drop user 用戶名 [cascade].如果要刪除的用戶中有模式對象,必須使用cascade.
一般企業開發中,建表要手動指定表空間,可以讓不同模塊、不同功能的對象存儲在不同的數據文件中,可以提高性能。
13.2 刪除用戶
--刪除用戶及其下面所有的對象
drop user itcasttest cascade;
每個數據庫用戶帳戶具備:
一個唯一的用戶名
一個驗證方法
一個默認的表空間
一個臨時表空間
權限和角色(dba用戶授權)
(每個表空間的配額.)
13.3 配置角色和權限
此時,用戶jack雖然創建,但沒有任何權限,連登陸都不能成功.因此需要使用dba用戶授予jack用戶權限。
Oracle內置有大量的權限,其中常見的權限有:
但是一個個的添加權限太過于麻煩,通過Oracle的預定義內置角色就可以實現將普通用戶的權限賦予給一個用戶。
普通用戶就選擇:connect和Resource角色。
管理員用戶選擇:connect和Resource、dba角色。
--給jack用戶授予普通用戶的權限
grant connect,resource to jack;
-------------------------------------------------------------------------
建立一個普通用戶的過程:
1. create user ...創建用戶(指定表空間)
2. 賦予權限(connect,resource)
13.4 操作jack用戶
--創建序列sequence成功
create sequence seq_test01; --序列名,其他使用默認值
select seq_test01.nextval from dual;
--創建表空間tablespace失敗
create tablespace tbs_jack_dat --表空間名
datafile --表空間類型
'c:/tbs_jack_dat01.dbf' size 50M --指定表空間數據文件的存儲位置及大小
autoextend on --開啟表空間文件自動增長
next 5M maxsize 1000M --每次增長5M,最大1000M
extent management local; --本地管理表空間
jack用戶創建表空間失敗,原因是表空間創建的權限不夠,表空間默認有dba權限的用戶才能創建。
13.5 對象權限
Oracle用戶的權限分為兩種:
系統權限(System Privilege): 允許用戶執行對于數據庫的特定行為,例如:創建表、創建用戶等
對象權限(Object Privilege): 允許用戶訪問和操作一個特定的對象,例如:對其他方案下的表的查詢
實現跨域訪問:
跨域訪問也稱之為跨用戶訪問、跨方案訪問,訪問的方式為:用戶名.對象名,
如在itcast用戶下訪問scott用戶下的emp表的數據:
Select * from scott.emp;
--訪問失敗。原因:沒有對象訪問權限。
登陸scott用戶,授予emp表的查詢權限給jack用戶
--scott用戶授予emp表的查閱權限給jack
grant select on emp to jack;
--跨域訪問成功。
select * from scott.emp;
注意:
賦權的時候,只能是自己擁有的權限、或者該權限是可以傳遞的,才可以將其賦予別人。
14. 視圖View
需求:jack用戶現在只需要查詢10部門的員工數據就行了,scott也不想將所有數據都開放給jack用戶。
14.1 視圖的概念
概念:
- 視圖是一種虛表.
- 視圖建立在已有表的基礎上, 視圖賴以建立的這些表稱為基表。
- 向視圖提供數據內容的語句為 SELECT 語句, 可以將視圖理解為存儲起來的 SELECT 語句.
- 視圖向用戶提供基表數據的另一種表現形式
作用:
- 限制數據訪問
- 簡化復雜查詢(將比較復雜并且經常要查詢的數據封裝成視圖保存起來,以后只需要查這張表即可)
- 提供數據的相互獨立
- 同樣的數據,可以有不同的顯示方式
- 視圖不能提高性能
14.2 語法
視圖只能創建、替換和刪除,不能修改。
14.3 視圖的操作
創建視圖:
嘗試在jack用戶創建視圖:(失敗)
--復制scott用戶的emp表
create or replace table emp as select * from scott.emp;
--在jack用戶創建視圖View
create view v_jack_emp --視圖的名稱
as select empno 編號,ename 姓名 from emp --
with read only;
創建jack用戶的視圖失敗,原因:權限不夠,說明視圖的創建權限也需要dba權限.
登陸system用戶,賦予jack用戶dba權限.
--賦予jack用戶dba權限
grant dba to jack;
--jack用戶再次創建視圖View
create or replace View v_jack_emp --視圖的名稱
as select empno 編號,ename 姓名 from emp where deptno=10 --視圖顯示內容
with read only;
創建成功。通過視圖查詢。
create or replace 創建或替換
刪除視圖:
--刪除視圖v_jack_emp
drop view v_jack_emp;
視圖默認是可以修改的,但我們一般只用視圖來提供查詢功能,因此設置它為只讀,with read only
14.4 視圖的跨域訪問
需求:在scott用戶下訪問jack用戶的視圖v_jack_emp
視圖和表類似,如果直接在scott用戶訪問該視圖肯定是沒有權限的,因此jack用戶要賦予scott查詢該視圖的權限。
--因為剛剛刪除了視圖,這里再次創建
create or replace view v_jack_emp --創建或替換視圖v_jack_emp
as select empno 編號,ename 姓名 from emp where deptno=10 --視圖的內容
with read only; --視圖只讀
--jack用戶賦予scott用戶視圖查詢權限
grant select on v_jack_emp to scott;
--登陸scott用戶執行查詢
select * from jack.v_jack_emp;
這樣就能控制讓其他用戶只能訪問我想讓他看到的內容,不給它查詢表的權限,只給他查詢視圖的權利。
實際上,我們查詢的表可能不是真正的表,而是視圖,而且還是只讀的。
視圖還可以屏蔽篩選/修改不同字段、字段名稱等(別名),因此,你看到的時候的字段也未必是真實表中存在的!
14.5 視圖小結
視圖和表的區別:
視圖是實體表的映射,視圖和實體表區別就是于視圖中沒有真實的數據存在。
什么時候使用視圖:
1,在開發中,有一些表結構是不希望過多的人去接觸,就把實體表映射為一個視圖。
2,在項目過程中,程序人員主要關注編碼的性能、業務分析這方面。對于一些復雜的SQL語句,提前把這些語句封裝到一個視圖中,供程序人員去調用
注意:查詢的對象(表)他可能不是一張的表,可能是視圖;你看到的視圖的字段可能也不是真實的字段。
15. 同義詞SYNONYM
需求:想偽裝一下這個視圖的名字不被其他人知道,或者是嫌調用的這個對象名字太長,怎么辦?
15.1 同義詞的概念
同義詞就是(對象的)別名,可以對表、視圖等對象起個別名,然后通過別名就可以訪問原來的對象了。
作用:
- 方便訪問其它用戶的對象
- 縮短對象名字的長度
15.2 語法
create [public] synonym for Object; --Object指Oracle對象
15.3 操作同義詞
創建同義詞:
首先,我創建一個tom用戶,檢測同義詞synonym的創建權限
--創建tom用戶
create user tom --用戶名稱
identified by orcl --密碼
default tablespace tbs_itdream_dat --默認表空間
temporary tablespace tbs_itdream_tmp; --臨時表空間
--授權
grant connect,resource to tom;
--創建tom01表格
create table tom01(id number);
insert into tom01 values(1);
select * from tom01;
--為tom01表格創建同義詞
create synonym t1 for tom01;
創建失敗,權限不夠.說明同義詞synonym的創建不夠權限,
下面刪除tom用戶,繼續使用jack用戶進行操作.
drop user tom cascade; //必須有dba權限的用戶才能刪除
------------------------------------------------------------------------
在擁有dba權限的jack用戶中為v_jack_emp視圖創建同義詞synonym:
--為視圖v_jack_emp創建同義詞
create synonym e for v_jack_emp;
--jack通過同義詞查詢視圖
select * from e;
--scott用戶通過同義詞跨域訪問視圖
select * from jack.e;
select * from jack.v_jack_emp;
一個小失誤:
不小心,將dba權限給刪除了,重裝了一次oracle,重建jack用戶
--創建默認表空間
create tablespace tbs_jack_dat --創建永久表空間tbs_jack_dat,
datafile --表空間的類型
'c:/tbs_jack_dat01.dbf' size 100M --指定表空間物理文件的存儲位置及默認大小
autoextend on --開啟自動增容
next 5M maxsize 2000M --自動增容一次5M,最大2G
extent management local;--本地管理表空間
--創建臨時表空間
create temporary tablespace tbs_jack_tmp --創建臨時表空間
tempfile --表空間類型為臨時表空間
'c:/tbs_jack_tmp01.dbf' size 5M --設置臨時表空間物理文件的儲存位置及默認大小,臨時表空間無需設置自動增長,有默認的貪吃模式
extent management local; --本地管理表空間
--創建jack用戶
create user jack --用戶名
identified by jack --密碼
default tablespace tbs_jack_dat --默認表空間
temporary tablespace tbs_jack_tmp --默認臨時表空間;
--授予權限給jack用戶
grant dba,connect,resource to jack;
--建表
create table emp as select * from scott.emp;
--------------------------------------------------
--創建視圖View
create or replace view v_jack_emp --創建視圖v_jack_emp
as select empno 編號,ename 姓名 from emp --視圖的內容
with read only; --設置視圖只讀
--給視圖創建同義詞
create synonym e for v_jack_emp;
--同義詞查詢測試(成功)
select * from e;
-----------------------------------------------------
--跨域查詢同義詞,不授權視圖,只授權同義詞查詢,測試是否能夠成功在scott用戶下查詢
--授權同義詞給scott
grant select on e to scott;
--切換scott用戶跨域查詢jack用戶的同義詞e代表的視圖v_jack_emp
select * from jack.e;--成功
select * from jack.v_jack_emp;--成功
結論:
授權視圖查詢給scott用戶,在scott用戶下通過視圖或同義詞查詢都可以。
授權同義詞查詢給scott用戶,同樣可以。
16. 索引-index
16.1 索引的概念特性和作用
數據庫中的索引相當于字典的目錄(索引)),它的作用就是提升查詢效率。
特性:
- 一種獨立于表的模式(數據庫)對象, 可以存儲在與表不同的磁盤或表空間中。
- 索引被刪除或損壞, 不會對表(數據)產生影響, 其影響的只是查詢的速度。
- 索引一旦建立, Oracle 管理系統會對其進行自動維護, 而且由 Oracle 管理系統決定何時使用索引. 用戶不用在查詢語句中指定使用哪個索引。
- 在刪除一個表時, 所有基于該表的索引會自動被刪除。
- 如果建立索引的時候,沒有指定表空間,那么默認索引會存儲在哪個表空間.會存儲在所屬用戶默認的表空間.
作用:
- 通過指針(地址)加速Oracle 服務器的查詢速度。
- 提升服務器的i/o性能(減少了查詢的次數)
16.2 索引的工作原理
16.3 操作索引
創建索引:
索引有兩種創建方式:
- 自動創建: 在定義 PRIMARY KEY 或 UNIQUE 約束后系統自動在相應的列上創建唯一性索引。
- 手動創建: 用戶可以在其它列上創建非唯一的索引,以加速查詢。
手動創建索引:
--scott用戶在emp表上的deptno上創建索引,
create index idx_scott_deptno on emp(deptno);
建立索引成功,說明索引的創建普通用戶的權限既可以完成。
上面索引的創建有缺陷。
--缺點:沒有指定表空間,生產環境下一般要將索引單獨指定表空間。
create index idx_emp_ename on EMP (ename) tablespace USERS;
刪除索引:
--刪除idx_scott_deptno索引
drop index idx_scott_deptno;
16.4 索引的創建場景
索引不是萬能!
以下情況可以創建索引:
- 列中數據值分布范圍很廣.即根據這個索引分類,可以分很多類。
- 表經常被訪問而且數據量很大 ,訪問的數據大概占數據總量的2%到4%
以下情況不要創建索引:
- 表比較小
- 列不經常作為連接條件或出現在WHERE子句中
- 查詢的數據大于2%到4%
- 表經常頻繁更新(看需求,如果表經常不斷的再更新,Oracle會頻繁的重新改動索引,反而降低了數據庫性能。但如系統日志歷史表,就必須增加索引,效率超高)
一些關于索引的問題:
1. 索引的作用是什么?
主要是提高查詢效率,減少磁盤的讀寫,從而提高數據庫性能。
2. 創建索引一定能提高查詢速度么?
未必!得看你創建的索引的合理性和語句的編寫
3. 索引創建的越多越好么?
不是!索引也是需要占用存儲空間的,過多的索引不但不會加速查詢速度,反而還會降低效率。
17. PL/SQL編程
17.1 概念和目的
PLSQL(Procedure Language/SQL)
是Oracle
對sql語言的過程化擴展。
PLSQL
在SQL
命令語言中增加了過程處理語句(如分支、循環等),使SQL語言具有過程處理能力。(具有編程的能力)。
17.2 PLSQL
的入門Hello World
declare --用于聲明變量
begin --業務邏輯
--輸出Hello World!
dbms_output.put_line('Hello World!');
end;
----------------------------------------------------------------------------
--面向過程的語言
--declare --聲明部分:沒有變量,則declare可以省略
--不需要變量聲明,則不需要寫任何東西
BEGIN--程序體的開始:編寫語句邏輯
--在控制臺輸出一句話:dbms_output相當于system.out類,內置程序包,put_line:相當于println()方法
dbms_output.put_line('Hello World');
--dbms_output.put('Hello World');
end;--程序體的結束
17.3 程序結構
PL/SQL可以分為三個部分:聲明部分、可執行部分、異常處理部分。
[delare]
聲明部分(變量、游標、例外)
begin
邏輯執行部分(DML語句、賦值、循環、條件等)
[exception]
異常處理部分(when 預定義異常錯誤 then)
end;
/
注意:在SQLPLUS中,PLSQL執行時,要在最后加上一個 “/”
17.4 變量聲明
declare聲明部分可以定義變量,定義變量的語法:
變量名 [CONSTANT] 數據類型;
* 普通數據類型(char, varchar2, date, number, boolean, long):
* id number;
* name varchar(20);
* sex char(1);
* birthday date;
* married boolean := true; 直接賦值
* salary number(7,2); 總共7位數,5位整數,2位小數
* 特殊變量類型(引用型變量,記錄型變量):
* username emp.ename%type; --引用型變量,即username的變量名和emp表的ename類型一樣
* emp_rec emp%rowtype; --記錄型變量,即一次可以存儲一行數據
17.5 賦值
普通變量賦值:
在ORACLE中有兩種賦值方式:
1,直接賦值語句 :=
2, 使用select …into … 賦值:(語法;select 值 into 變量)
-----------------------------------------------------------------------------
declare --變量聲明
v_id number;
v_name varchar(20) := 'jack'; --直接賦值
v_address varchar(60);
v_salary number;
begin --邏輯代碼
--方法一:直接賦值
v_id := 1;
--方法二:語句賦值
select '深圳' into v_address from dual;
select sal into v_salary from emp where ename='KING';
--打印
dbms_output.put_line('v_id:'||v_id||',姓名:'||v_name||',地址:'||v_address||',薪資:'||v_salary);
end;
注意:
語句賦值,格式是select 字段 into 聲明變量 from 表 where ...
引用變量:引用表中字段的類型 (推薦使用引用類型)
%type 例: v_ename emp.ename%type;
--查詢并打印7839號(老大)員工的姓名和薪水
declare
--變量引用emp表的字段類型
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
--語句賦值v_ename
select ename into v_ename from emp where empno=7839;
--語句賦值v_sal
select sal into v_sal from emp where empno=7839;
--打印
dbms_output.put_line('姓名:'||v_ename||',薪資:'||v_sal);
end;
------------------------------------------------------------------------
引用類型的好處:
- 使用普通變量定義方式,需要知道表中列的類型,而使用引用類型,不需要考慮列的類型
- 使用引用類型,當列中的數據類型發生改變,不需要修改變量的類型。而使用普通方式,當列的類型改變時,需要修改變量的類型
使用%TYPE
是非常好的編程風格,因為它使得PL/SQL更加靈活,更加適應于對數據庫定義的更新。
記錄型變量:
記錄型變量,代表一行,可以理解為數組,里面元素是每一字段值。
%rowtype 例: v_emp emp%rowtype;直接引用emp表所有的字段及類型
含義:v_emp 變量代表emp表中的一行數據的類型,它可以存儲emp表中的任意一行數據。
---------------------------------------------------------------------------
--查詢并打印7839號(老大)員工的姓名和薪水
declare
--引用類型變量
v_emp emp%rowtype;--該變量可以存儲emp表中一行記錄的值
begin
--變量語句賦值
select * into v_emp from emp where empno=7839;
--打印
dbms_output.put_line('姓名:'||v_emp.ename||',薪資:'||v_emp.sal);
end;
17.6 PLSQL編程—流程控制
條件結果 if
語法:
if 條件 then 結果
elsif 條件 then 結果
......
else 其他情況的結果
end if;
----------------------------------------------------------------------------
--判斷emp表中記錄是否超過20條,,10-20之間,10以下打印一句
declare
--聲明一個變量,存儲查詢出來的記錄數
v_count number;
begin
--獲取emp表的記錄數,給v_count賦值
select count(1) into v_count from emp;
--條件判斷
if v_count<=10 then dbms_output.put_line('記錄數小于10條');
elsif v_count>10 and v_count<=20 then dbms_output.put_line('記錄數在10-20之間');
else dbms_output.put_line('記錄數大于20條');
end if;
end;
循環Loop
在ORACLE中有三種循環:
Loop 循環 EXIT WHEN...條件 end loop;
While()…loop 條件判斷循環
For 變量 in 起始..終止 Loop
其中使用第一種,能完成其它兩種循環,因此接下來就是用Loop循環操作。
--打印數字1-10
declare
--聲明一個變量
v_num number := 1;
begin
--循環
loop
--輸出
dbms_output.put_line(v_num);
--退出循環的條件
exit when v_num >= 10;
v_num := v_num+1;
end loop;
end;
18. 游標-Cursor
18.1 游標的概念
游標從概念上講基于數據庫的表返回結果集,也可以理解為游標就是個結果集,但該結果集是帶向前移動的指針的,每次只指向一行數據。類似與JDBC操作時返回的ResultSet結果集。
游標的主要作用:
用于臨時存儲一個查詢返回的多行數據(結果集),通過遍歷游標,可以逐行訪問處理該結果集的數據。
游標的使用方式:聲明--->打開--->讀取--->關閉
18.2 語法
1. 游標聲明:
CURSOR 游標名 [ (參數名 數據類型[,參數名 數據類型]...)]
IS SELECT 語句;
無參游標:
cursor c_emp is select ename from emp;
有參游標:
cursor c_emp(v_deptno emp.deptno%TYPE) is select ename from emp where deptno=v_deptno;
2. 開啟游標. 會執行查詢獲得結果集。
Open 游標名(參數列表)
例:open c_emp; --開啟游標,執行查詢
3. 讀取游標
fetch 游標名 into 變量列表|記錄型變量
例:fetch c_emp into v_ename;--取一行游標的值到變量中。注意:v_ename必須與emp表中的ename列類型一致。(v_ename emp.ename%type;)
4. 關閉游標釋放資源
例:close 游標名
close c_emp;--關閉游標釋放資源
18.3 游標的屬性
游標的原理:
游標剛開啟時,游標在結果集的第一條記錄之前。
當fetch取值時,指針會往前游動,并獲取游動后的后處于游標位置的值。(游標是有位置的)
注意:游動不能回頭。
因此可以使用,%notfound屬性來判斷游標是否遍歷完畢。
18.4 操作無參游標
需求:使用游標查詢emp表中所有員工的姓名和工資,并將其依次打印出來。
引用型變量獲取游標的值:
declare
--聲明一個游標
cursor c_emp_test1
is select ename,sal from emp;
--聲明兩個變量用于接收遍歷出來的數據
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
--開啟游標,執行查詢
open c_emp_test1;
--使用游標,循環取值
loop
--獲取值,存入臨時變量時,要保證數量和類型一致.每次取出值后,指針往下移動一次
fetch c_emp_test1 into v_ename,v_sal;
--判斷當前游標位置是否有值,如果沒有就退出循環
exit when c_emp_test1%notfound;
--輸出
dbms_output.put_line('姓名:'||v_ename||',薪資:'||v_sal);
end loop;
--關閉游標,釋放資源
close c_emp_test1;
end;
PLSQL程序運行結果:
使用記錄型變量存值:
declare
--聲明游標
cursor c_emp is select * from emp;
--記錄型變量
v_emp emp%rowtype;--可用于存儲一行記錄的值
begin
--開啟游標,執行查詢
open c_emp;
--使用游標,循環取值
loop
--獲取游標的值放入變量的時候,必須要into前后要對應(數量和類型)
fetch c_emp into v_emp;
--推出循環的條件
exit when c_emp%notfound;
--打印
dbms_output.put_line('姓名:'||v_emp.ename||',薪資:'||v_emp.sal);
end loop;
--關閉游標
close c_emp;
end;
18.5 帶參游標
需求:使用游標查詢并打印某部門的員工的姓名和薪資,部門編號為運行時手動輸入。
使用引用型變量取游標的值:
declare
--聲明游標--帶參數的游標:需要定一個形式參數
cursor c_emp_dept(v_deptno emp.deptno%type)
is select ename,sal from emp where deptno=v_deptno;
--聲明變量
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
--開啟游標,執行查詢--傳入參數:部門編號
open c_emp_dept(10);
--使用游標循環取出游標中的值
Loop
--取值,存入臨時變量(保證into后的數量與類型與游標的select一致)
fetch c_emp_dept into v_ename,v_sal;
--退出循環的條件:如果當前游標位置沒有值,就退出循環
exit when c_emp_dept%notfound;
--輸出
dbms_output.put_line('姓名:'||v_ename||',薪資:'||v_sal);
end Loop;
--關閉游標釋放資源
close c_emp_dept;
end;
使用記錄型變量取游標中的值:
declare
--聲明一個帶參的游標
cursor c_emp_dept2(v_deptno emp.deptno%type)
is select * from emp where deptno=v_deptno;
--記錄型變量
v_emp emp%rowtype;
begin
--開啟游標,執行查詢獲得結果集
open c_emp_dept2(20);--傳入參數:部門編號20
--使用游標,循環取值
Loop
--取值(保證數量和類型一致,因此游標使用的*)
fetch c_emp_dept2 into v_emp;
--跳出循環(指針所處游標位置沒有值時跳出循環)
exit when c_emp_dept2%notfound;
--打印
dbms_output.put_line(v_emp.ename||'>>>'||v_emp.sal);
end Loop;
--關閉游標,釋放資源
close c_emp_dept2;
end;
注意:
- Found是游標有數據的判斷。如果游標剛打開,值false
- Notfound是游標結束的判斷,如果游標剛打開,值false,只判斷游標是否結束!
19.存儲過程
19.1 概念與作用
存儲過程:就是一塊PLSQL語句包裝起來,起個名稱
相對而言:單純plsql可以認為是匿名程序。
- plsql是存儲過程的基礎。
- java是不能直接調用plsql的,但可以通過存儲過程這些對象來調用。
19.2 語法
create or replace procedure 過程名(參數列表)
as/is
--聲明
begin
PLSQL子程序體,完成邏輯操作
end 程序名;
-----------------------------------------------------------------------------
As和is是通用的。
根據參數的類型,我們將其分為3類:
1. 不帶參數的存儲過程
2. 帶輸入參數的存儲過程
3. 帶輸入參數與輸出參數的存儲過程
19.3 無參的存儲過程
create or replace procedure sayHelloWorld --沒有參數的情況下,不要加()
as
--聲明變量,不寫不可省略
begin
dbms_output.put_line('Hello World!');
end sayHelloWorld;
存儲過程的測試,直接在Procedure對象右鍵選擇Test即可。
19.4 存儲過程的調用方法
如何調用執行,兩種方法:
- 用exec(execute)命令來調用—用來測試存儲
- 用其他的程序(plsql和java)來調用
命令調用的方式:
程序調用:
19.5 帶輸入參數的存儲過程
可以直接在PLSQL工具右鍵新建Procedure程序,工具幫我們自動寫好一些代碼。
--查詢并打印某個員工(如7839號員工)的姓名和薪水--存儲過程:要求,調用的時候傳入
--創建一個帶輸入參數的存儲過程,in代表這是個輸入參數,out代表是輸出參數
create or replace procedure p_queryempsal(i_empno in emp.empno%type)
is
--聲明變量, 接收結果
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
--邏輯代碼
select ename,sal into v_ename,v_sal from emp where empno=i_empno;
--輸出打印
dbms_output.put_line('姓名:'||v_ename||',薪資是:'||v_sal);
end p_queryempsal;
命令調用:
程序調用:
19.6 帶輸入in和輸出參數out—主要是其他程序調用
--輸入員工號查詢某個員工(7839號員工)信息,要求,將薪水作為返回值輸出,給調用的程序使用。
create or replace procedure p_queryempsal_out(i_empno in emp.empno%type,
o_sal out emp.sal%type)
is
begin
--執行查詢
select sal into o_sal from emp where empno=i_empno;
end p_queryempsal_out;
--------------------------------------------------------------------------
調用(使用plsql程序調用):
--使用plsql程序調用p_queryempsal_out存儲過程
declare
--輸入參數值
v_empno emp.empno%type := 7839;
--聲明一個參數來接收輸出參數
v_sal emp.sal%type;
begin
--調用Procedure存儲過程
p_queryempsal_out(v_empno,v_sal);--第二個參數是輸出參數,必須有變量來接受!
--打印v_sal
dbms_output.put_line('員工編號:'||v_empno||',薪資是:'||v_sal);
end;
注意:調用的時候,參數要與定義的參數的順序和類型一致.
----------------------------------------------------------------------------
也可以直接Test測試。
存儲過程總結:
存儲過程的作用:主要用來執行一段程序。
- 在開發程序中,為了一個特定的業務功能,會向數據庫進行多次連接關閉(連接和關閉是很耗費資源)。這種就需要對數據庫進行多次I/O讀寫,性能比較低。如果把這些業務放到PLSQL中,在應用程序中只需要調用PLSQL就可以做到連接關閉一次數據庫就可以實現我們的業務,可以大大提高效率.
- ORACLE官方給的建議:能夠讓數據庫操作的不要放在程序中。在數據庫中實現基本上不會出現錯誤,在程序中操作可以會存在錯誤.(如果在數據庫中操作數據,可以有一定的日志恢復等功能.)
三種存儲基本應用場景:
- 無參參數:只用來做數據處理。存儲內部寫一些處理數據的邏輯。
- 帶輸入參數:數據處理時,可以針對輸入參數的值來進行判斷處理
- 帶輸入輸出參數:一般用來傳入一個參數值,我想經過數據庫復雜邏輯處理后,得到我想要的值然后輸出給我。
20.存儲函數
語法:
create [or replace] function 函數名(參數列表)
return 函數值類型
as/is
--聲明
begin
--程序體
--必須有return
return ...;
end;
20.1 存儲函數案例
--查詢某職工的總收入。
create or replace function queryempincome(f_empno in emp.empno%type) --創建一個存儲函數,傳遞參數
return number --返回值類型
as
--聲明一個變量查詢出來的結果返回
v_income number;
begin
--查詢賦值
select sal*12+nvl(comm,0) into v_income from emp where empno=f_empno;
--返回值
return v_income;
end;
PLSQL程序調用:
--測試queryempincome存儲函數
declare
--聲明一個變量接收返回的值
v_income number;
begin
--調用函數獲得結果
v_income := queryempincome(7934);--傳入參數
--打印
dbms_output.put_line('編號:'||7934||',總收入:'||v_income);
end;
Test程序測試:
如何選擇存儲過程和存儲函數?
原則上,如果只有一個返回值,用存儲函數,否則,就用存儲過程。
但是,一般我們會直接選擇使用存儲過程,原因是:
* 函數是必須有返回值,存儲過程可以有也可以沒有,存儲的更靈活!
* 存儲過程也可以有輸出參數,可以代替存儲函數。
* Oracle的新版本中,已經不推薦使用存儲函數了。
21. 例外
語法:
declare
--聲明部分
begin
--邏輯部分
excepetion
--捕獲例外
when 例外名 then ...
when 例外名 then...
when others then ...
end;
--例外
declare
i number;
--聲明一個引用型變量
v_ename emp.ename%type;
--聲明一個記錄型變量
v_emp emp%rowtype;
begin
--i := 1/0; zero_divide
--i := 'abc'; value_error
--select ename into v_ename from emp where empno = 123; no_data_found
select * into v_emp from emp;
exception
when zero_divide then dbms_output.put_line('發生了除零例外');
when value_error then dbms_output.put_line('算術或轉換例外');
when no_data_found then dbms_output.put_line('沒有找到數據例外');
when too_many_rows then dbms_output.put_line('記錄數不匹配例外');
when others then dbms_output.put_line('發生了未知例外');
end;
21.1 自定義例外
在declare-begin中聲明一個自定義例外:
例外名 exception;
----------------------------------------------------------------------------
--使用自定義例外
declare
--聲明自定義例外
no_emp_found exception;
--聲明游標
cursor c_emp is select * from emp;
v_emp emp%rowtype;
begin
--開啟游標
open c_emp;
--因為要使用自定義游標,直接利用游標的屬性拋異常
loop
fetch c_emp into v_emp;
if c_emp%notfound then
raise no_emp_found;
end if;
end loop;
exception
when no_emp_found then
dbms_output.put_line('拋出自定義例外');
when others then
dbms_output.put_line('拋出未知的例外');
--關閉游標
close c_emp;
end;
22. 使用java調用存儲過程
寫一個存儲過程p_queryempsal_out
.使用java代碼調用它,并獲取到返回的值打印出來。
PLSQL代碼:
--需求:封裝存儲過程:傳入某個員工的員工編號,輸出該員工的年薪
create or replace procedure proc_getYearSal(i_empno in emp.empno%type,o_yearsal out emp.sal%type) --創建存儲過程
as
--聲明部分
begin
--獲取年薪,賦值給輸出變量
select sal*12+nvl(comm,0) into o_yearsal from emp where empno=i_empno;
end;
---------------------------------------------------------------------------
java代碼調用過程:
使用JDBC連接數據庫,傳入員工編號,獲取參數。
1. 加載數據庫驅動
2. 創建Connection連接
3. 獲取執行sql的CallableStatement對象
4. 設置參數
5. 執行sql,獲取結果集
6. 關閉資源
首先導入Oracle.jdbc的驅動包:ojdbc6.jar
準備JDBCUtils工具類:(變量也可以提取到配置文件中)
public class JDBCUtils {
private static String driver = "oracle.jdbc.oracle.OracleDriver";
private static String url = "jdbc:oracle:thin:@192.168.175.10:1521:orcl";
private static String user = "jack";
private static String password = "jack";
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;// why? ---> Java GC
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
st = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
----------------------------------------------------------------------------
java代碼調用存儲過程-輸出參數返回的值是普通類型:
@Test
public void test() throws Exception {
// 獲取連接對象
Connection connection = JDBCUtils.getConnection();
String sql = "{call proc_getYearSal(?,?)}"; // 轉義sql,從API中查詢格式即可
// 獲取執行sql的對象CallableStatement
CallableStatement call = connection.prepareCall(sql);
// 設置參數,輸入參數直接set,輸出參數需要注冊,執行完之后,call對象中調用get方法獲取輸出值
call.setInt(1, 7369);
// 注冊輸出參數.參數1:參數的位置,參數2:參數類型
call.registerOutParameter(2, OracleTypes.DOUBLE);
// 執行sql
call.execute();
//獲取輸出參數的值
double yearSal = call.getDouble(2);
System.out.println("編號7369的員工的年薪為:"+yearSal+"元");
//關閉資源
JDBCUtils.release(connection, call, null);
}
java代碼調用存儲過程-輸出參數返回的值是結果集:
--需求:封裝存儲過程,獲取emp表所有員工的編號,姓名,月薪
create or replace procedure proc_getempinfo(c_empinfo out sys_refcursor) --創建存儲過程,輸出參數是一個結果集,因此用游標封裝(sys_refcursor系統引用游標,使用時才指定查詢語句)
as
begin
--系統引用游標,開啟時指定查詢條件
open c_empinfo for select empno,ename,sal from emp;
end;
使用java代碼調用該存儲過程proc_getempinfo:
@Test
public void test2() throws Exception {
// 獲取連接
Connection connection = JDBCUtils.getConnection();
String sql = "{call proc_getempinfo(?)}";// 轉義sql
// 獲取執行sql的對象
CallableStatement call = connection.prepareCall(sql);
// 設置參數:注冊輸出參數
call.registerOutParameter(1, OracleTypes.CURSOR);
// 執行sql
call.execute();
// 獲取結果集.
//CallableStatement沒有getCursor方法,找它的實現類對象
//System.out.println(call.getClass()); //oracle.jdbc.driver.T4CCallableStatement
//但是它的實現類權限是默認的而不是public的而不能使用,因此使用它的父類OracleCallableStatement中的getCursor方法
OracleCallableStatement call2 = (OracleCallableStatement)call;
ResultSet rs = call2.getCursor(1);
while(rs.next()) {
System.out.println("編號是:"+rs.getObject("empno"));
System.out.println("姓名是:"+rs.getObject("ename"));
System.out.println("月薪是:"+rs.getObject("sal"));
System.out.println("=================================");
}
//關閉資源
JDBCUtils.release(connection, call, rs);
}
- 數據庫存儲過程的編寫,注意輸出參數必須加上out
- 誰調用存儲過程,得到游標結果集ResultSet由調用者負責關閉游標。
23. 觸發器-Trigger
23.1 概念與作用
數據庫觸發器是一個與表相關聯的、存儲的PL/SQL程序。
每當一個特定的數據操作語句(Insert,update,delete)在指定的表上發出時,Oracle自動地執行觸發器中定義的PLSQL語句序列。
換句話說:觸發器就是在執行某個操作(增刪改)的時候觸發一個動作(一段程序)。
23.2 語法
創建觸發器語法:
CREATE [or REPLACE] TRIGGER 觸發器名
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF 列名]}
ON 表名
[FOR EACH ROW [WHEN(條件) ] ]
PLSQL 塊(即:as begin end)
23.3 觸發器HelloWorld
測試:普通用戶也可以創建觸發器。
create or replace trigger tri_sayHello --創建觸發器
before --在..操作之前執行觸發器
insert --插入數據時觸發
on emp --插入emp表時觸發
declare
--聲明部分
begin
--邏輯部分
dbms_output.put_line('Hello World!!!');
end;
23.3 觸發器的類型
- 語句級觸發器(表級觸發器)
- 在指定的操作語句操作之前或之后執行一次,不管這條語句影響了多少行 。
- 行級觸發器(FOR EACH ROW)
- 觸發語句作用的每一條記錄都被觸發。在行級觸發器中使用old和new偽記錄變量, 識別值的狀態。
23.4 語句級觸發器與行級觸發器的區別
--語句級觸發器:
create or replace trigger tri_yuju_test
before update
on emp
declare
begin
dbms_output.put_line('語句級觸發器...');
end;
--行級觸發器:
create or replace trigger tri_hangji_test
before update
on emp
for each row --定義行級觸發器
declare
begin
dbms_output.put_line('行級觸發器...');
end;
delete from emp where empno in(9527,9528);
select * from emp;
--測試:修改emp表所有員工的工資:
update emp set sal=10000;
語句級觸發器和行級觸發器區別:
語法上:
1.行級觸發器需要定義:for each row
表現上:
2.行級觸發器,在每一行的數據進行操作的時候都會觸發。但語句級觸發器,對表的一個完整操作才會觸發一次。
簡單的說:行級觸發器,是對應行操作的;語句級觸發器,是對應表操作的。
上面的區別,是在一條sql語句控制改變表時才會發生。
如果,通過insert一條一條的向表中插入數據,它們就都會觸發。
23.5.行級別觸發器的偽記錄變量
行級觸發器的強大之處:可以獲取修改前后的值。
上面的表格很容易理解,只有update修改前后才都有值。
如果是insert插入,插入前是沒有值的(null).
如果是delete刪除,刪除后是沒有值的(null).
需求:使用觸發器,保證漲后的工資不能少于漲前的工資
分析:使用行級觸發器,能夠獲取修改前后的值進行比較,如果漲后工資還低的話,拋出例外(異常)
create or replace trigger tri_addsal --創建觸發器
before update on emp --定義觸發條件
for each row --定義行級觸發器
declare
begin
if :new.sal<=:old.sal then
raise_application_error(-20001,'漲后工資不能低于漲前的工資!?。?);
end if;
end;
測試:
update emp set sal=sal-100;
23.6. 觸發器的應用場景及注意事項
觸發器可應用于:
- 數據確認
- 實施復雜的安全性檢查
- 做審計,跟蹤過表上所做的數據操作等
- 數據的備份與同步
觸發器的注意事項:
觸發器會引起鎖,降低效率!使用時要慎重。如無必要,盡量不要使用觸發器
行級觸發器會引發行級鎖(鎖行數據)
語句級觸發器可能會引起表級鎖(鎖表)
存儲過程練習:
- 寫一個存儲過程,輸出久久乘法表
首先可以先寫出java代碼,然后根據java代碼寫出PLSQL代碼
public void test3() {
for(int i=1;i<=9;i++) {
for(int j=1;j<=i;j++) {
System.out.print(j+"*"+i+"="+j*i+" ");
}
System.out.println();
}
}
九九乘法表的存儲過程:
create or replace procedure proc_ninemul --創建一個無參的存儲過程
is
--聲明兩個變量
i number := 1;
j number;
begin
--邏輯部分
loop
exit when i > 9;--退出外循環的條件
j := 1; --每次執行內循環之前,將j設為1
loop
exit when j>i;--退出內循環的條件
dbms_output.put(i||'*'||j||'='||i*j||' ');--內循環共輸出一行
j := j+1;
end loop;
--每次執行完內循環換行
dbms_output.put_line('');
i := i+1;
end loop;
end proc_ninemul;
--調用
begin
proc_ninemul;
end;