大型数据库
?
over(order by salary) 按照salary排序進行累計,order by是個默認的開窗函數
over(partition by deptno)按照部門分區
?
?
如何使用Oracle Round 函數 (四舍五入)
描述 : 傳回一個數值,該數值是按照指定的小數位元數進行四舍五入運算的結果。
SELECT ROUND( number, [ decimal_places ] ) FROM DUAL
參數:
number : 欲處理之數值
decimal_places : 四舍五入 , 小數取幾位 ( 預設為 0 )
Sample :
select round(123.456, 0) from dual; 回傳 123
select round(123.456, 1) from dual; 回傳 123.5
?
ratio_to_report主要完成對百分比的計算,語法為
ratio_to_report(exp) over()
也就是根據over窗口函數的作用區間,求出作用區間中的單個值在整個區間的總值的比重
比如要求scott用戶下emp表中每個員工的工資占本部門的比重
select ename,sal,deptno,ratio_to_report(sal) over(partition by deptno) ratio from emp;
?
select deptno,
ename,
sal,
sum(sal) over (partition by deptno order by sal, ename) cum_sal,
round(100*ratio_to_report(sal) over (partition by deptno), 1) pct_dept,
round(100*ratio_to_report(sal) over (), 1) pct_over_all
from emp order by deptno, sal;
?
?
Oracle 的體系結構
?
Oracle的服務由數據庫和實例組成
?
實例就是一組操作系統進程(或者是一個多線程的進程)和一些內存,這些進程可以操作數據庫。數據庫只是一個文件集合(包括數據文件、控制文件、重做日志文件)。
實例可以在任何時間點裝載和打開一個數據庫。實際上,準確地講,實例在其整個生存期中最多能裝載和打開一個數據庫!
連接數據庫實例: 建立用戶連接和創建會話
?
?
?
物理存儲結構包括存儲在磁盤上的數據文件、控制文件、重做日志文件
?
內存結構:SGA和PGA
SGA:System Global Area是Oracle Instance的基本組成部分,在實例啟動時分配;系統全局域SGA主要由三部分構成:共享池、數據緩沖區、日志緩沖區等包括兩個可選的(大型池和java池)。
SGA_MAX_SIZE指的是可動態分配的最大值﹐而SGA_TARGET是當前已分配的最大SGA。
?
共享池:Shared Pool用于緩存最近被執行的SQL語句和最近被使用的數據定義,主要包括:Library cache(共享SQL區)和Data dictionary cache(數據字典緩沖區)。? 共享SQL區是存放用戶SQL命令的區域,數據字典緩沖區存放數據庫運行的動態信息。內存大小由參數SHARED_POOL_SIZE指定。Library cache(共享SQL區)存儲了最近使用的SQL和PL/SQL語句。數據字典緩沖區是最近在數據庫中使用的定義的集合。
緩沖區高速緩存:Database Buffer Cache用于緩存從數據文件中檢索出來的數據塊,可以大大提高查詢和更新數據的性能。塊的大小由參數DB_BLOCK_SIZE決定。
重做日志緩沖區:redo log buffer cache 記錄了在數據庫數據塊中做的所有改變。
大型池:Large Pool是SGA中一個可選的內存區域,它只用于shared server環境。
Java池:Java Pool為Java命令的語法分析提供服務。
?
PGA:Process Global Area是為每個連接到Oracle database的用戶進程保留的內存。
?
?
進程是操作系統中的一種機制,它可執行一系列的操作步。
Oracle進程中有三類:用戶進程,服務進程,后臺進程
用戶進程: 當用戶請求連接oracle服務時啟動。它必須首先建立一個連接,它不能和oracle 服務直接交互。
?
服務進程:可以直接和oracle 服務進行交互。
?
后臺進程:
DBWR??數據庫寫入程序
該進程執行將緩沖區寫入數據文件,是負責緩沖存儲區管理的一個OR
ACLE后臺進程。
?
?
?
?
LGWR?日志寫入程序?
該進程將日志緩沖區寫入磁盤上的一個日志文件,它是負責管理日志
緩沖區的一個ORACLE后臺進程。
在數據庫寫入程序之前
?
?
SMON?系統監控
該進程實例啟動時執行實例恢復,還負責清理不再使用的臨時段。
?
?
PMON??進程監控
該進程在用戶進程出現故障時執行進程恢復,負責清理內存儲區和釋
放該進程所使用的資源。
?
CKPT?檢查點?
該進程在檢查點出現時,對全部數據文件的標題進行修改,指示該檢
查點。
使用檢查點的原因:
檢查點確保在內存中頻繁改變的數據塊可以正常的寫入數據文件
可以快速的進行實例的恢復
?
ARCH?歸檔
該進程將已填滿的在線日志文件拷貝到指定的存儲設備。當日志是為
ARCHIVELOG使用方式、并可自動地歸檔時ARCH進程才存在。?
Oracle的邏輯結構表明了數據庫的物理結構是如何被使用的
數據庫->表空間->段->區->數據塊
?
表空間:
保留相關數據庫對象的組
Oracle數據庫中典型表空間包括
SYSTEM表空間
DATA表空間
USER表空間
TOOLS表空間
TEMP表空間
?
數據庫的控制空間分配
為數據庫用戶設置空間配額
備份或恢復數據
?
段:定義為分配給邏輯數據庫結構的擴展區集合
不同類型的段:
數據段
索引段
回滾段
臨時段
?
?
?
?
?
?
?
模式:所謂模式是指一系列邏輯數據結構或對象的集合。
模式與用戶相對應,一個模式只能被一個數據庫用戶所擁有,并且模式的名稱和用戶的名稱相同。
Oracle數據庫中并不是所有的對象都是模式對象。表,索引,約束,索引,視圖,序列,存儲過程,同義詞,用戶自定義數據結構,數據庫連接。而表空間,用戶,角色,目錄,概要文件及上下文等數據庫對象不屬于任何模式,稱為非模式對象。
?
?
Oracle語句的處理過程
解析->優化->行資源生成->執行
解析:對提交的語句進行語法和語義檢查
將已經提交的語句分解,判定屬于那種類型,并在其上執行各種檢驗操作
語法檢查:正確表述,符合SQL規則
語義檢查:正確應用SQL對象?授權?歧義?
檢查Shared Pool:已被其他Session處理過
?
優化:生成一個可在oracle中用來執行語句的最佳計劃
行資源生成:為回話取得最佳計劃和建立執行計劃
執行:完成實際執行查詢的行資源生成步驟的輸出
?
存儲在常規表中行采用沒有特定的次序存儲
Oracle將獲取的名字與ROWID進行關聯。ROWID是表中行的物理地址,可以告知對象的來源,所處的文件以及文件中特定數據塊。
?
?
?
Oracle常用的有兩種索引類型:B樹索引和位圖索引
B樹索引:B樹索引是最常用的索引,它的存儲結構類似于書的目錄索引結構,有分支節點和葉子節點,分支節點相當于書的大目錄,葉子節點相當于具體到頁的索引。B樹索引是oracle數據庫的默認索引類型。
只有幾個不同的值供選擇。例如,一個"類型"列中,只有四個不同的值(A,B,C,和D)。該索引是一個低效的選擇。如果你有一個Oracle數據庫,那么為這些選擇范圍小的的列建立位圖索引是更好的選擇。
?
?
?
數據庫啟動和關閉
為了滿足數據庫管理的需要,Oracle數據庫的啟動和關閉是分步驟進行的
STARTUP
ALTER DATABASE db01 MOUNT
ALTER DATABASE db01 READ ONLY
STARTUP RESTPICT
ALTER SYSTEM ENABLE RESTRICTED SESSION
?
?
?
?
?
?
?
?
?
當數據庫被創建的時候,oracle 服務在數據文件創建額外的對象結構
數據字典表
動態性能表
?
Oracle數據字典是由表和視圖組成,存儲有關數據庫結構信息的一些數據庫對象。
數據字典按照存在的形式分為數據字典表和數據字典視圖。
數據字典
基表是存儲有關數據庫的信息的底層表。基表是在任何 Oracle 數據庫中首先創建的對象。在使用 CREATE DATABASE 創建數據庫時,只要 Oracle 服務器運行 sql.bsq 腳本,就會自動創建這些對象。
數據字典視圖
數據字典視圖是基表的匯總,可以更有效地顯示基表信息。
?
?
?
數據字典內容包括:
1,數據庫中所有模式對象的信息,如表、視圖、簇、索引、集群、同義詞、序列、過程、方法、包、觸發器等。
2,分配多少空間,當前使用了多少空間等。
3,列的缺省值。
4,約束信息的完整性。
5,Oracle用戶的名字。
6,用戶及角色被授予的權限。
7,用戶訪問或使用的審計信息。
8,其它產生的數據庫信息。
?
?
數據字典有三個主要的用途
Oracle服務器使用它來發現關于使用者,模式對象,存儲結構的信息。
Oracle 服務器修改它當DDL語句執行時。
用戶和DBAs可以把它當作只讀表來查看數據庫的信息。
?
?
?
?
?
為了便于用戶對數據字典表的查詢, Oracle對這些數據字典都分別建立了用戶視圖,這樣即容易記住,還隱藏了數據字典表表之間的關系,Oracle針對這些對象的范圍,分別為:
DBA:所有方案中的視圖(可以看到所有數據字典里的信息)
ALL:用戶可以訪問的視圖(可以看到所有這個用戶可以看到的信息)
USER:用戶方案中的視圖(僅僅是這個用戶擁有的信息)
三者直接的關系:
?
?
動態性能視圖記錄當前數據庫的活動
只要數據庫正在操作視圖就會持續的更新
信息是從內存和控制文件中獲得的
DBAs使用動態視圖來監視和調整數據庫
SYS 用戶才能使用動態視圖
不允許有DML操作
?
在oracle數據庫中,用戶權限分為下列兩個類
系統權限
允許用戶在數據庫中執行特定的操作
對象權限
允許用戶讀取和操作特定的對象
?
總共有超過100個不同的系統權限
ANY關鍵字 可以擁有所有的權限
GRANT關鍵字 可以增加權限
REVOKE關鍵字 回收權限
?
Eg:GRANT CREATE SESSION TO emi;
????GRANT CREATE SESSION TO emi WITH ADMIN OPTION;
只用授權時帶有WITH ADMIN OPTION 子句時,用戶才可以將獲得的系統權限再授予其他用戶,即系統權限的傳遞性。
?
系統權限的回收
REVOKE CREATE TABLE FROM emi;
不管師是否有 WITH ADMIN OPTION,它都不會產生連級的影響
?
?
對象權限
回收對象權限使用WITH GRANT OPTION將會有連級的影響。
?
?
?
角色
角色的好處
更簡單的權限管理
動態的權限管理
選擇可供使用的權限
可以通過操作系統授權
提高性能
?
Eg:CREATE ROLE oe_clerk;
ALTER ROLE oe_clerk IDENTIFIED BY order;
????GRANT oe_clerk TO scott;
ALTER USER scott DEFAULT ROLE hr_clerk,oe_clerk
CREATE ROLE admin role IDENTIFIED USING hr.employee
????SET ROLE hr_clerk;
????SET ROLE oe_clerk IDENTIFIED BY order;
????REVOKE oe_clerk FROM scott;
????REVOKE hr_manager FROM PUBLIC;
????DROP ROLE hr_manager;
?
?
Oracle提供的語句
條件語句
CASE 表達式
?
CASE expr WHEN comparison_expr1 THEN return_expr1
[WHEN comparison_expr2 THEN return_expr2
WHEN comparison_exprn THEN return_exprn
ELSE else_expr]
END
?
DECODE 函數
decode(條件,值1,返回值1,值2,返回值2,...值n,返回值n,缺省值)
該函數的含義如下:
IF 條件=值1 THEN
RETURN(翻譯值1)
ELSIF 條件=值2 THEN
RETURN(翻譯值2)
......
ELSIF 條件=值n THEN
RETURN(翻譯值n)
ELSE
RETURN(缺省值)
END IF
decode(字段或字段的運算,值1,值2,值3)
?????? 這個函數運行的結果是,當字段或字段的運算的值等于值1時,該函數返回值2,否則返回值3
當然值1,值2,值3也可以是表達式,這個函數使得某些sql語句簡單了許多
使用方法:
1、比較大小
select decode(sign(變量1-變量2),-1,變量1,變量2) from dual; --取較小值
sign()函數根據某個值是0、正數還是負數,分別返回0、1、-1
例如:
變量1=10,變量2=20
則sign(變量1-變量2)返回-1,decode解碼結果為"變量1",達到了取較小值的目的。
?
?
?
?
Merge命令
通過這個merge你能夠在一個SQL語句中對一個表同時執行inserts和updates操作. 當然是update還是insert是依據于你的指定的條件判斷的,Merge into可以實現用B表來更新A表數據,如果A表中沒有,則把B表的數據插入A表. MERGE命令從一個或多個數據源中選擇行來updating或inserting到一個或多個表
merge into products p using newproducts np on (p.product_id = np.product_id)
when matched then
update set p.product_name = np.product_name
when not matched then
insert values(np.product_id, np.product_name, np.category)
?
TRUNC(number,num_digits)
Number 需要截尾取整的數字。
Num_digits 用于指定取整精度的數字。Num_digits 的默認值為 0。
?
?
Read Consistency(讀一致性)
?
?
?
?
?
?
?
?
?
?
集合類型 ?
?
使用條件:
?? a. 單行單列的數據,使用標量變量 。
?? b. 單行多列數據,使用記錄
?? c. 單列多行數據,使用集合
?????? *集合:類似于編程語言中數組也就是。pl/sql集合類型包括關聯數組Associative array(索引表 pl/sql table)、嵌套表(Nested Table)、變長數組(VARRAY)。
?
Nested table與VARRY既可以被用于PL/SQL,也可以被直接用于數據庫中,但是Associative array不行
Associative array是不能通過CREATE TYPE語句進行單獨創建,只能在PL/SQL塊(或Package)中進行定義并使用(即適用范圍是PL/SQL Block級別)
Nested table與VARRAY則可以使用CREATE TYPE進行創建(即適用范圍是Schema級別),它們還可以直接作為數據庫表中列的類型
?
數字有三種基本類型:
NUMBER可以描述整數或實數
PLS_INTEGER和BINARY_INTENER只能描述整數
NUMBER,是以十進制格式進行存儲的,但在計算中系統會自動轉換成為二進制進行運算。例如:NUMBER(5,2)可以用來存儲表示-999.99...999.99間的數值
BINARY_INTENER用來描述不存儲在數據庫中,但是需要用來計算的帶符號的整數值。它以2的補碼二進制形式表述。循環計數器經常使用這種類型。
PLS_INTEGER和BINARY_INTENER唯一區別:
在計算當中發生溢出時,BINARY_INTENER型的變量會被自動指派給一個NUMBER型而不會出錯,PLS_INTEGER型的變量將會發生錯誤
?
DECLARE
????TYPE ib_planguage IS TABLE OF VARCHAR2(10) INDEX BY PLS_INTEGER;
????lang ib_planguage;
????idx PLS_INTEGER;
BEGIN
????lang(1):='java';
????lang(9):='c#';
????lang(3):='c++';
????idx:=lang.FIRST;
????WHILE(idx IS NOT NULL) LOOP
????????DBMS_OUTPUT.PUT_LINE(lang(idx));
????????idx:=lang.NEXT(idx);
????END LOOP;
END;
?
與Associative array不同,Nested table變量需要顯式初始化。Nested table初始化之后還需要調用EXTEND過程,擴展集合的"容量"
DECLARE
????TYPE nt_planguage IS TABLE OF VARCHAR2(10);
????lang nt_planguage;
BEGIN
????lang:=nt_planguage('java','c#','c++');
????FOR i IN 1..lang.COUNT LOOP
????????DBMS_OUTPUT.PUT_LINE(lang(i));
????END LOOP;
END;
?
?
VARRAY可以在聲明時限制集合的長度。其索引總是連續的,而Nested table的索引在初始化賦值時是連續的,不過隨著集合元素被刪除,可能變得不連續
DECLARE
????TYPE va_planguage IS VARRAY(8) OF VARCHAR2(10);
????lang va_planguage;
BEGIN
????lang:=va_planguage('java','c#','c++');
????FOR i IN 1..lang.COUNT LOOP
????????DBMS_OUTPUT.PUT_LINE(lang(i));
????END LOOP;
END;
?
首選是Associative array,因為它不需要初始化或者EXTEND操作,并且是迄今為止最高效的集合類型。唯一不足的一點是它只能用于PL/SQL而不能直接用于數據庫。
如果你需要允許使用負數索引,應該選擇Associative array;
如果你需要限制集合元素的個數,應該選擇VARRAY
?
?
?
DUAL表
dual是一個虛擬表,用來構成select的語法規則
select user from dual;
select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;--獲得當前系統時間
select SYS_CONTEXT('USERENV','TERMINAL') from dual;--獲得主機名
select SYS_CONTEXT('USERENV','language') from dual;--獲得當前 locale
select dbms_random.random from dual;--獲得一個隨機數
select your_sequence.nextval from dual;--獲得序列your_sequence的下一個值
select your_sequence.currval from dual;--獲得序列your_sequence的當前值
select 7*9 from dual; --用做計算器
?
?
ASCII 返回與指定的字符對應的十進制數
CHR 給出整數,返回對應的字符
CONCAT 連接兩個字符串
INITCAP 返回字符串并將字符串的第一個字母變為大寫
INSTR(C1,C2,I,J) 在一個字符串中搜索指定的字符,返回發現指定的字符的位置
LENGTH 返回字符串的長度
LOWER 返回字符串,并將所有的字符小寫
UPPER 返回字符串,并將所有的字符大寫
RPAD和LPAD 粘貼字符
ADD_MONTHS增加或減去月份
LAST_DAY 返回日期的最后一天
MONTHS_BETWEEN 給出date2-date1的月份
?
?
PL/SQL基礎
?
概述:
PL/SQL是Oracle對SQL的一種擴充,集成了程序化設計語言中的許多特性
PL/SQL的特點
過程化和模塊化
使用過程化語言的控制結構
錯誤處理
可移植性
集成
改善性能
支持所有的事物控制命令
支持所有SQL的DML命令
支持所有SQL的DDL命令
支持所有SQL的DCL命令
支持所有SQL的數據類型、函數、各種運算符
PL/SQL程序塊可以存儲在服務器中,被其他程序或SQL命令調用
對PL/SQL程序塊可以進行權限管理
?
?
PL/SQL中的Select語句
SELECT 列名,列名 . . . INTO 變量1,變量2 . . .
FROM 表 WHERE 條件 . . . ;
在使用SELECT …INTO…時,結果只能有一條,如果返回了多條數據或沒有數據,則將產生錯誤。(對于多條記錄的遍歷,可以使用游標)
?
PL/SQL程序結構
- PL/SQL程序的單元由邏輯塊(BLOCK)組成
- 塊可以順序出現,也可以相互嵌套
- 每一個塊分成三部分
[DECLEAR]
????-- 說明部分(可選的)
BEGIN
????-- 語句執行部分(必需的)
[EXCEPTION]
????-- 出錯處理程序(可選的)
END ;
/
?
PL/SQL類型
無名塊:嵌入在應用內或交互式發出的無名塊
有名塊(應用的過程、函數):可以接受參數,并返回結果的有名塊,存儲在應用程序中。可以被反復調用。
存儲過程、函數:可以接受參數,并返回結果的有名塊,存儲在服務器端。可以被反復調用
包:有名的PL/SQL模塊,是相關的過程、函數、標識符的集合。存儲在服務器端。可以被反復調用。
數據庫出發器:與數據庫表相關的PL/SQL塊,存儲在服務器端。在客戶與服務器觸發事件發生時自動觸發。
應用觸發器:與一個應用事件相關的PL/SQL塊,存儲在服務器端。在應用程序的觸發事件時自動觸發。
?
?
變量定義
變量 [CONSTANT] 數據類型 [NOT NULL] [:= DEFAULT PL/SQL 表達式];
V_num????????number(2)????????:= 12 ;
沒有賦初值的變量,初值都是NULL
?
變量類型
%TYPE和%ROWTYPE 類型
%TYPE:表示已經定義的變量類型定義
%ROWTYPE:表示已經定義的表、視圖中的紀錄的類型或游標的結構類型
優點
不必了解數據庫中列的個數和數據類型
如果表結構改變,PL/SQL程序可以不變,減少程序的維護工作
V_EMPNO ????EMP.EMPNO%TYPE; /*EMP表名,EMPNO列名*/
V_REC????????EMP%ROWTYPE; /*EMP可以是表名或游標名*/
?
?
用戶定義的類型
create or replace type student_type as object(
????id????????number(5) ,
????firstname????varchar2(20) ,
????lastname????varchar2(20) ,
????major????????varchar2(30) ,
????current_credits????number(3));
????v_stu????????student_type ;
?
?
變量賦值
變量名:=常量 或 PL/SQL表達式
可在說明部分賦值,也可在執行部分賦值
DECLEAR
????num_var????number(5):=5; /*說明部分賦值*/
????v_emp????????emp%rowtype;
BEGIN
????v_emp.empno := 11011 ;???? /*執行部分賦值*/
????……
select sal into num_var from emp where empno = 7788 ;
?
?
變量的作用范圍
變量如果不在子塊中重新定義,則在PL/SQL塊的所有子塊中有效
?
如果變量在子塊內重新定義,子塊內定義的變量優先,此變量的作用范圍僅在本子塊內有效。
?
如果主塊中的變量A和子塊變量A中同時定義時,在子塊中要用主塊的變量A時,必須在變量前加塊的標識符(如:塊的標識符.A)。
?
常用內置函數
函數 說明 轉換前的類型
TO_CHAR 轉換成VARCHAR類型 數字型、日期型
TO_DATE 轉換成DATE 字符型
TO_NUMBER 轉換成number類型 字符型
?
SELECT TO_CHAR (current_credits) INTO v_1 FROM student2 WHERE id = 1002
?
?
條件語句:
IF語句
DECLARE
????v_sal????emp.sal%type ;
BEGIN
????SELECT sal INTO v_sal FROM emp WHERE empno = 7788;
????IF v_sal < 500 THEN
????????UPDATE emp SET sal = sal * 1.5 WHERE empno = 7788;
????ELSIF v_sal < 1500 THEN
????????UPDATE emp SET sal = sal * 1.3 WHERE empno = 7788;
????ELSE
????????UPDATE emp SET sal = sal * 1.0 WHERE empno = 7788;
????END IF;
????COMMIT;
END;
/
<條件>是一個布爾型變量或表達式,取值只能是TRUE/FALSE/NULL
?
CASE語句:
CASE ????
????WHEN grade= 'A' THEN dbms_output.put_line('excellent');
????WHEN grade= 'B' THEN dbms_output.put_line('very good');
????WHEN grade= 'C' THEN dbms_output.put_line('good');
????WHEN grade= 'D' THEN dbms_output.put_line('fair');
????WHEN grade= 'F' THEN dbms_output.put_line('poor);
????ELSE dbms_output.put_line(' no such grade');
END CASE;
?
?
循環語句
Loop語句
DECLARE
????v_counter number :=1;
BEGIN
????LOOP
???? INSERT INTO temp_table VALUES(v_counter, 'loop index') ;
???? v_counter := v_counter + 1;
???? IF v_counter > 50 THEN
????????exit
???? END IF;
????END LOOP;
END;
/
?
?
WHILE語句
DECLARE
????v_counter number := 1 ;
BEGIN
????WHILE v_counter <= 50 LOOP
????????INSERT INTO temp_table VALUES(v_counter, 'loop_index');
????????v_counter := v_counter + 1 ;
????END LOOP;
END;
/
?
FOR語句
DECLARE
????????v_counter number :=1 ;
BEGIN
????FOR v_counter IN 1..50 LOOP
????????INSERT INTO temp_table VALUES(v_counter,'loop index');
????END LOOP ;
END;
/
FOR v_counter IN REVERSE 1..50 LOOP
其中:IN:表示索引變量的值小到大 IN REVERSE:表示索引變量的值從大到小
?
?
GOTO語句
DECLARE
????v_vonter number := 1;
BEGIN
????LOOP
????????INSERT INTO temp_table VALUES(v_counter, 'loop count');
????????v_counter := v_counter + 1 ;
????????IF v_counter > 50 THEN
????????????GOTO ENDOFLOOP ;
????????END IF ;
????END LOOP ;
????<<ENDOFLOOP>>
????????INSERT INTO temp_table(char_col) VALUES('done!') ;
END;
?
對于塊、循環或IF語句而言,想要從外層跳到內層是非法的
從一個IF子句調轉到IF的另一個子句中是非法的
出錯處理塊不能實行跳轉
?
?
?
異常處理
一個PL/SQL塊的出錯處理(EXCEPTION)部分包含程序處理多個錯誤的代碼。當一個錯誤發生時,程序控制離開PL/SQL塊的執行部分轉移到出錯處理部分。
. . .
BEGIN
. . .
EXCEPTION
????WHEN 錯誤1 [ OR 錯誤2 ] THEN 語句序列 1 ;
????WHEN 錯誤3 [ OR 錯誤4 ] THEN 語句序列 2 ;
????. . .
????WHEN OTHERS THEN 語句序列 3 ;
END ;
?
?
系統預定義錯誤 :在PL/SQL中經常出現的25個系統定義的錯誤,不必定義,允許服務器隱式地出發它們,只需要在出錯處理部分處理它們
用戶自定義錯誤:開發者認為是非正常的一個條件,必須在說明部分定義,在執行部分顯示觸發它們,在出錯處理部分處理它們。
?
異常代碼 異常名稱 說明
ORA-01403 NO_DATA_FOUND 查詢沒有返回數據
ORA-01422 TOO_MANY_ROWS SELECT….INTO 語句返回多行結果
DECLARE
????v_comm emp.comm%type ;
BEGIN
????SELECT????comm????INTO????v_comm
????FROM????emp????WHERE????empno = 7788 ;
EXCEPTION
????WHEN no_data_found THEN
????????????dbms_output.put_line( 'no data ! ' ) ;
????WHEN too_many_rows THEN
????????????dbms_output.put_line( 'too many row ! ' ) ;
????WHEN others THEN
????????????null ;
END ;
?
?
用戶自定義的錯誤有三個步驟:定義錯誤,觸發錯誤,處理錯誤
DECLARE
????e_toomanystudent???? exception; /*定義錯誤*/
????v_currentstudent????????number(3);
????v_maxstudent????????number(3);
????v_errorcode????????number;
????v_errortext????????varchar2(200);
BEGIN????
SELECT current_student , max_students INTO
????v_currentstudent , v_maxstudent
FROM classe????WHERE department = 'HIS' and course = 101 ;
IF v_currentstudent > v_maxstudent THEN
????RAISE????e_toomanystudent ; /*觸發錯誤*/
END IF ;
EXCEPTION
?
?
WHEN no_data_found or too_many_rows THEN
????dbms_output.putline('發生系統預定義錯誤') ;
WHEN e_toomanystudent THEN????????/*處理錯誤*/
????INSERT INTO log_table(info)
????VALUES('history 101 has' || v_currentstudent);
WHEN others THEN
????v_errorcode = sqlcode ;
????v_errortext = substr(sqlerrm,1,200)
????INSERT INTO log_table(code , message , info )
????VALUES(v_errorcode , v_errortext ,'Oracle error occured');
END ;
/
?
?
?
游標
游標是一個指向內存區域的指針,一個PL/SQL結構
游標有兩種類型:顯示游標和隱式游標
顯示游標:是由程序員定義和命名的,并且在塊的執行部分中通過特定語句操作的內存工作區。
隱式游標:是由PL/SQL為DML語句和SELECT語句隱式定義的工作區
?
?
顯示游標的處理步驟
?
游標是一個不含INTO子句的SELECT語句
SELECT語句允許帶WHERE,ORDER BY,GROUP BY等子句。
游標的定義
DECLARE
CURSOR emp_cursor IS
SELECT empno, ename
???? FROM emp;
CURSOR dept_cursor IS
SELECT *
FROM dept
WHERE deptno = 10;
BEGIN
...
?
打開游標
OPEN 游標名;
?
游標的處理
FETCH游標名INTO????[變量1, 變量2, ...]| 紀錄名];
FETCH emp_cursor INTO v_empno, v_ename;
?
DECLARE
???? CURSOR c_classes IS SELECT * FROM class;
v_classes c_classes%rowtype ;
BEGIN
???? . . .
OPEN c_classes;
FETCH c_classes INTO v_classes ;
. . .
END;
/
?
完成對行數據處理后,可以關閉游標,如果需要,還可以再次打開游標,關閉游標后,系統釋放與該游標關聯的資源,不能再對游標進行FETCH操作。
CLOSE????????c_classes;
?
顯示游標的一些屬性
%ISOPEN,%NOTFOUND,%FOUND,%ROWCOUNT
%ROWCOUNT Number 值是當前為止返回的記錄數,初值為0,每取一條記錄,該屬性值加1。
?
LOOP
FETCH emp_cursor INTO v_ename , v_sal;
EXIT WHEN emp_cursor%ROWCOUNT>5 or emp_cursor%NOTFOUND;
...
END LOOP;
?
?
DECLARE
v_deptno emp.deptno%type :=&p_deptno;
v_ename emp.ename%type;
v_sal emp.sal%type;
CURSOR emp_cursor IS SELECT ename ,sal FROM emp
????????????????????????????????WHERE deptno = v_deptno;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO v_ename , v_sal;
EXIT WHEN emp_cursor%NOTFOUND;
INSERT INTO temp(ename, sal) VALUES(v_ename,v_sal);
END LOOP;
CLOSE emp_cursor;
COMMIT;
END;
/
?
?
?
游標的FOR循環
使用游標的FOR循環,可以簡化游標的操作步驟
游標的FOR循環隱式(自動)地完成三個步驟:打開游標;(FETCT)取數據;關閉游標
記錄名師系統隱式定義的游標名%ROWTYPE類型的記錄變量,不必事先定義
FOR 紀錄名 IN 游標名 LOOP
????????語句1;
????語句2;
????...
END LOOP;
?
DECLARE
v_deptno emp.deptno%type := &p_deptno;
CURSOR emp_cursor IS SELECT ename ,sal FROM emp
????????????????????????????????????WHERE deptno = v_deptno;
BEGIN
FOR emp_record IN emp_cursor LOOP
INSERT INTO temp(ename , sal)
VALUES(emp_record.ename , emp_record.sal);
END LOOP;
COMMIT; /*在本段程序中,沒有打開游標、取數據、關閉游標語句*/
END;
/
?
要操縱數據庫中數據,在定義游標的查詢語句時,必須加上FOR UPDATE OF從句,表示要對表加鎖。
表加鎖后,在UPDATE或DELETE語句中,加WHERE CURRENT OF子句,既可以對鎖定的數據進行修改
CURSOR 游標名IS
SELECT 列1,列2 … FORM 表 WHERE 條件
FOR UPDATE [OF column][NOWAIT];
?
帶WHERE CURRENT O 從句的UPDATE語句和DELETE語句
DELETE FROM 表 WHERE CURRENT OF 游標名;
UPDATE 表 SET 列1=值1,列2=值2... WHERE CURRENT OF 游標名;
?
Eg:查詢emp表某部門的雇員情況,如果雇員的工資小于800,則將其工資改為800
DECLARE
v_deptno emp.deptno%type :=&p_deptno;
v_empno emp.empno%type;
v_job emp.job%type;
v_sal emp.sal%type;
CURSOR emp_cursor IS SELECT ename,job,sal FROM emp
????????????????WHERE deptno = v_deptno FOR UPDATE OF sal;
BEGIN
FOR emp_record IN emp_cursor LOOP
IF emp_record.sal < 800 THEN
UPDATE emp SET sal=800 WHERE CURRENT OF emp_cursor;
END IF;
dbms_output.put_line(emp_record.empno||emp_record.sal);
END LOOP;
COMMIT;
END;
?
?
Eg:為職工增加10%的工資,從最低工資開始長,增加后工資總額限制在50萬以內
DECLARE
emp_num number :=0; s_sal emp.sal%type;
e_sal emp.sal%type; e_empno emp.empno%type;
CURSOR c1 IS SELECT empno,sal FROM emp ORDER BY sal
???????????????? FOR UPDATE OF sal;
BEGIN
OPEN c1;
SELECT sum(sal) INTO s_sal FROM emp;
WHILE s_sal < 500000
LOOP
FETCH c1 INTO e_empno,e_sal;
EXIT WHEN c1%NOTFOUND;
s_sal := s_sal + e_sal*0.1;
UPDATE emp SET sal=sal*1.1 WHERE CURRENT OF c1;
?
emp_num := emp_num + 1;
END LOOP;
CLOSE c1;
INSERT INTO msg VALUES(emp_num,s_sal);
COMMIT;
END;
?
?
帶參數的游標
此參數只能在游標的查詢語句中使用
只能向游標傳遞參數數值,不能通過參數帶出結果
可以有選擇地給參數提供一個默認值
CURSOR 游標名(參數1 數據類型[{:=|DEFAULT}值]
[,參數2 數據類型[{:=|DEFAULT]值] ...]
IS SELECT 語句 ;
?
DECLARE
CURSOR emp_cursor
(p_deptno NUMBER, p_job VARCHAR2) IS
SELECT????empno, ename????FROM????emp
WHERE????deptno = p_deptno
AND ????job = p_job;
BEGIN
OPEN emp_cursor(10, 'CLERK');
...
?
Eg:首先查詢DEPT表取出所有的部門號,然后根據DEPT表中返回的每一個部門號,從emp表查詢該部門的雇員信息
DECLARE
CURSOR dept_cursor IS SELECT deptno FROM dept;
v_ename emp.ename%type;
v_sal emp.sal%type;
CURSOR emp_cursor(v_deptno number) IS
SELECT ename, sal FROM emp WHERE deptno = v_deptno;
BEGIN
FOR dept_record IN dept_cursor LOOP
EXIT WHEN dept_cursor%NOTFOUND ;
OPEN emp_cursor(dept_record.deptno);
LOOP
FETCH emp_cursor INTO v_ename,v_sal;
EXIT WHEN emp_cursor%NOTFOUND;
dbms_output.put_line(dept_record.deptno ||' '||
' '||v_sal);
END LOOP;
CLOSE emp_cursor;
END LOOP;
COMMIT;
END;
?
?
隱式游標
由系統定義,不需要用戶定義
用來處理INSERT,UPDATE,DELETE和單行的SELECT…INTO語句
游標名為SQL
游標的屬性存儲有關最近一次SQL命令的狀態信息
BEGIN
UPDATE emp SET sal=10000,deptno=30 WHERE empno=9998;
IF SQL%NOTFOUND THEN
INSERT INTO emp(empno,sal,deptno) VALUES(9998,10000,30);
END IF;
COMMIT;
END;
?
隱式游標的游標屬性%ISOPEN總是FALSE,因為當語句執行完后立即關閉隱式游標
SELEC….INTO語句只能執一行
?
存儲過程
CREATE [OR REPLACE] PROCEDURE 過程名
[(參數名 [ IN | OUT | IN OUT ] 數據類型,... )]
{IS | AS}
????????????[說明部分]
BEGIN
????????????語句序列
????[EXCEPTION 出錯處理]
END [過程名];
OR REPLACE 是一個可選的關鍵字,表示替代原有的過程
IS或AS后面是一個完整的PL/SQL塊的三部分(說明部分,執行部分,異常處理部分)
IN表示輸入變量,OUT表示輸出變量,IN OUT表示輸入輸出變量,缺省表示IN
CREATE OR REPLACE PROCEDURE modetest(
p_inpara IN????number,
p_outpare OUT number,
p_inoutpara IN OUT number)
IS
v_local number;
BEGIN
v_local????????:= p_inpara; /* IN類型參數不能出現在:=的左邊 */
p_outpara????:= 7; /* OUT類型參數不能出現在:=的右邊 */
v_local????????:= p_inoutpara;
p_inoutpara????:= 7;
END;????
?
?
CREATE PROCEDURE raise_salary(emp_id interger,v_increase integer)
IS /* 缺省IN */
BEGIN
UPDATE emp SET sal = sal + v_increase
WHERE empno = emp_id;
COMMIT;
END;
?
如果IN OUT參數的值在過程中沒有被更改,則它返回到調用環境時值不變
如果IN OUT 參數是字符型,不能指定長度。長度有調用環境決定
?
?
函數
參數都是IN類型,存儲函數必須返回并且只返回一個結果
函數體的可執行部分必須有RETURN語句(RETURN 表達式)
表達式的數值類型與RETURN子句定義要一致
CREATE OR REPLACE FUNCTION get_sal (p_emp_no IN empno%TYPE)
RETURN NUMBER
IS
???? v_emp_sal emp.sal%TYPE :=0;
BEGIN
???? SELECT sal INTO v_emp_sal FROM emp WHERE empno=p_emp_no;
RETURN(v_emp_sal);
EXCEPTION
WHEN no_data_found or too_many_rows THEN
dbms_output.put_line('System Error');
WHEN others THEN
dbms_output.put_line(sqlerrm);
END get_sal;
/
?
?
CREATE OR REPLACE FUNCTION average_sal(v_n IN number(3))
RETURN NUMBER
IS
???? CURSOR c_emp IS SELECT empno ,sal FROM emp;
v_total_sal emp.sal%TYPE;
v_counter number;
v_emp_no emp.empno%TYPE;
BEGIN
FOR r_emp IN c_emp LOOP
EXIT WHEN c_emp%ROWCOUNT > v_n OR c_emp%NOTFOUND;
v_total_sal := v_total_sal + r_emp.sal;
v_counter := c_emp%ROWCOUNT;
v_emp_no := r_emp.empno;
dbms_output.putline('loop='||v_counter||
';empno='||v_emp_no);
END LOOP;
RETURN(v_total_sal/v_counter);
END average_sal;
/
?
過程和函數
參數類型不同:函數只有IN類型參數,而存儲有IN,OUT,IN OUT三個類型參數
返回值的方法不同:函數返回只有一個值,而存儲過程返回值由OUT參數帶出來
調用方法不同:
過程(實際參數1,實際參數2……);
變量名:=函數名(實際參數1,實際參數2….);
?
過程/函數中的異常處理
CREATE OR REPLACE PROCEDURE fire_emp(
p_emp_no IN emp.empno%TYPE)
IS
invalid_employee EXCEPTION;????????/*定義錯誤*/
BEGIN
DELETE FROM emp WHERE empno = p_emp_no ;
IF SQL%NOTFOUND THEN
RAISE invalid_employee; /*觸發錯誤*/
END IF;
EXCEPTION
WHEN invalid_employee THEN ROLLBACK;
????????INSERT INTO exception_table(line_nr,line)
????????VALUES(1,'employee does not exist.');
WHEN others THEN
dbms_output.putline(sqlerrm);
END fire_emp;
/
?
在SQL*Plus中使用過程/函數
在SQL*Plus中,用VARIABLE定義的變量在引用時,必須前面加冒號(:)。用ACCEPT接受的變量在引用時,前面加&符號。
?
SET SERVEROUTPUT ON
ACCEPT p_emp_no PROMPT 'please enter the employee number:'
VARIABLE v_emp_name varchar2(14);
VARIABLE v_emp_sal number;
VARIABLE v_emp_comm number;
EXECUTE query_emp(&p_emp_no,:v_emp_name,
:v_emp_sal,:v_emp_comm);
EXECUTE dbms_output.putline('Infomation for employee:'
|| to_char(&p_emp_no));
EXECUTE dbms_output.putline('The name is:'|| :v_emp_name);
EXECUTE dbms_output.putline('The salary is:'
|| to_char(:v_emp_sal));
EXECUTE dbms_output.putline(The commission is:'
|| to_char(:v_emp_comm));
?
?
?
包
包是一個可以將相關對象存儲在一起的PL/SQL結構
它包含了兩個分離的組成部分:包說明和包主體
包的組成
可以將相關的若干程序單元組織到一塊,用一個包名來標識這個集合
包中可以包含的程序單元
過程,函數,變量,游標,類型,常量,出錯情況
CREATE [OR REPLACE] PACKAGE 包名
{IS | AS}
公共變量的定義????????|
公共類型的定義????????|
公共出錯處理的定義????|
公共游標的定義????????|
函數說明????????????|
過程說明
END;
/
CREATE PACKAGE sal_package IS
PROCEDURE raise_sal(v_empno emp.empno%TYPE,
v_sal_increment emp.sal%TYPE);
PROCEDURE reduce_sal(v_empno emp.empno%TYPE,
v_sal_reduce emp.sal%TYPE);
v_raise_sal emp.sal%TYPE :=0;
v_reduce_sal emp.sal%TYPE :=0;
END;
/
?
CREATE [OR REPLACE] PACKAGE BODY 包名
{IS | AS}
私有變量的定義????????|
私有類型的定義????????|
私有出錯處理的定義????|
私有游標的定義????????|
函數定義????????????|
過程定義
END ;
/
CREATE PACKAGE BODY sal_package IS
PROCEDURE raise_sal(v_empno emp.empno%TYPE,
v_sal_increment emp.sal%TYPE)
IS
BEGIN
UPDATE emp SET sal = sal + v_sal_increment
WHERE empno = v_empno;
COMMIT WORK;
v_raise_sal := v_raise_sal + v_sal_increment;
END;
PROCEDURE reduce_sal(v_empno emp.empno%TYPE,
v_sal_reduce emp.sal%TYPE)
IS
BEGIN
UPDATE emp SET sal = sal – v_sal_reduce
WHERE empno = v_empno;
COMMIT WORK;
v_reduce_sal := v_reduce_sal + v_sal_reduce;
END;
END;
?
?
所有提供的Package由SYS所擁有,對于不是SYS的用戶,必須擁有EXCEUTE權限才能調用
所有Oracle提供的程序包都是以DBMS_或UTL_開頭
?
靜態SQL
靜態SQL指直接嵌入在PL/SQL塊中的SQL語句,靜態SQL用于完成特定或固定的任務。
select sal from emp where empno=4000;
?
動態SQL
動態SQL運行PL/SQL塊時動態輸入的SQL語句。如果在PL/SQL需要執行DDL語句,DCL語句,或則需要執行更加靈活的SQL語句(select中有不同where條件),需要用到用到動態SQL。
編寫動態SQL語句時,需要將SQL語句存放到字符串變量中,而且SQL語句可以包含占位符(以冒號開始)。
v_sql varchar2(100);
v_sql:='delete from emp where empno =:v_empno';
?
?
//刪除某個表
create or replace procedure pro_drop_table(v_table_name varchar2)
is
v_sql varchar2(100);
begin
v_sql := 'drop table ' || v_table_name;
execute immediate v_sql;
end;
?
BEGIN
FOR I IN 1 .. 100 LOOP
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE T'||I;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE 'CREATE TABLE T'||I||' ( DUMMY CHAR(1) )';
EXECUTE IMMEDIATE 'INSERT INTO T'||I||' VALUES ( ''X'' )';
END LOOP;
END;
/
?
將字符串每個逗號字符用方括號進行封裝
declare
t_vararray dbms_utility.lname_array;
vc_stringlist varchar2(4000);
n_idx binary_integer;
begin
--comma to table
vc_stringlist := 'dkf,dddl,fewe,klkj';
dbms_utility.comma_to_table(vc_stringlist, n_idx, t_vararray);
dbms_output.put_line('Total Num : '||to_char(n_idx));
?
for i in 1..n_idx loop
dbms_output.put_line(t_vararray(i));
t_vararray(i) := '['||t_vararray(i)||']';
end loop;
--table to comma
dbms_utility.table_to_comma(t_vararray, n_idx, vc_stringlist);
dbms_output.put_line('');
dbms_output.put_line(vc_stringlist);
?
end;
結果
Total Num : 4
dkf
dddl
fewe
klkj
[dkf],[dddl],[fewe],[klkj]
?
?
DECLARE
l_temp NUMBER;
BEGIN
l_temp := DBMS_UTILITY.get_time;
dbms_output.put_line('before=' || l_temp);
dbms_lock.sleep(5);
l_temp := DBMS_UTILITY.get_time;
dbms_output.put_line('after=' || l_temp);
END;
/
?
?
before=59955835
after=59956335
?
?
?
DECLARE
l_temp NUMBER;
BEGIN
l_temp := DBMS_UTILITY.get_cpu_time;
dbms_output.put_line('before=' || l_temp);
dbms_lock.sleep(5);
l_temp := DBMS_UTILITY.get_cpu_time;
dbms_output.put_line('after=' || l_temp);
END;
/
before=10
after=10
?
?
?
觸發器
觸發器由說明部分,語句執行部分和出錯處理部分三部分組成的PL/SQL有名塊(類似于存儲過程和函數)
觸發器不接受參數,不能再程序中調用
當觸發事件發生時隱式地(自動地)執行
觸發事件包括:在數據庫中執行如下操作
INSERT
UPDATE
DELETE
?
將超過某工種工資范圍的員工信息紀錄到aduit_message表中。sal_guide表紀錄了每一工種的工資范圍
CREATE OR REPLACE TRIGGER check_sal
BEFORE INSERT OR UPDATE OF sal,job ON emp FOR EACH ROW
WHEN (new.job <> 'PRESIDENT')
DECLAER
v_minsal sal_guide.minsal%TYPE;
v_maxsal sal_guide.maxsal%TYPE;
e_sal_out_of_range EXCEPTION;
BEGIN
SELECT minsal , maxsal INTO v_minsal , v_maxsal
FROM sal_guide WHERE job = :new.job;
IF (:new.sal < v_minsal) OR (:new.sal > v_maxsal) THEN
RAISE e_sal_out_of_range;
END IF;
EXCEPTION
WHEN e_sal_out_range THEN
INSERT INTO audit_message(line_nr,line)
VALUES(1,'salary'||to_char(:new.sal)||
'is out of range for employee'||to_char(:new.empno));
END ;
?
?
觸發器類型:語句級和行級
觸發事件:表的插入,更新,刪除
觸發時間:BEFORE和AFTER
?
觸發器分語句級和行級觸發器兩個級別
行級觸發器與語句級觸發器的區別主要在于觸發次數不同
如果DML語句只影響一行,則語句級和行級觸發器效果一樣
如果該DML語句影響多行,則行級觸發器觸發的次數比語句級觸發器觸發的次數多
?
語句之前觸發->行之前觸發->插入、更新、刪除一條記錄->行之后出發->語句之后觸發
?
在觸發器體內禁止使用COMMIT,ROLLBACK語句
?
事件指明觸發事件的數據操縱語句,有三種可能的值:INSERT,UPDATE,UPDATE OF 列名1,DELETE
該觸發器在一個數據操作語句發生時只觸發一次
CREATE [OR REPLACE] TRIGGER 觸發器名
{BEFORE | AFTER} 事件1 [OR 事件2 ...] ON 表名
PL/SQL 塊;
Eg:創建一個BEFORE型語句級觸發器。限制一周內向emp表插入數據的時間,如果是周六、周日,或晚上6點到第二天早上8點之間插入,則中斷操作,并提示用戶不允許在此時間向emp表插入
CREATE OR REPLACE TRIGGER secure_emp
BEFORE INSERT ON emp
BEGIN
IF (TO_CHAR(sysdate,'DY') IN( 'SAT','SUN')
OR (TO_CHAR(sysdate,'HH24') NOT BETWEEN '8' AND '18')
THEN RAISE_APPLICATION_ERROR(-20500,'you may only
insert emp during normal hours.');
END IF;
END; ????????
?
使用觸發謂詞(INSERTING,UPDATING,DELETING)
觸發器可以包含多個觸發事件,在觸發器中使用謂詞判斷是哪個觸發了觸發器
謂詞 行為和值
INSERTING????????????如果觸發事件是INSERTING,則謂詞的值為TRUE,否則為FALSE
?
?
CREATE OR REPLACE TRIGGER secure_emp
BEFORE DELETE OR INSERT OR UPDATE ON emp
BEGIN
IF (TO_CHAR(sysdate,'DY') IN( 'SAT','SUN') OR
(TO_CHAR(sysdate,'HH24') NOT BETWEEN '8' AND '18') THEN
IF DELETING THEN
????RAISE_APPLICATION_ERROR(-20502,'you may only
delete emp during normal hours.');
ELSIF INSERTING THEN
RAISE_APPLICATION_ERROR(-20500,'you may only
insert emp during normal hours.');
ELSE THEN
RAISE_APPLICATION_ERROR(-20504,'you may only
update emp during normal hours.');
END IF;
END IF;
END;????
?
?
通過在CREATE TRIGGER 語句中指定FOR EACH ROW 子句創建一個行觸發器
CREATE [OR REPLACE] TRIGGER 觸發器名
{BEFORE | AFTER} 事件1 [OR 事件2 ...] ON 表名
FOR EACH ROW [WHEN 限制條件]
PL/SQL 塊;
?
Eg:將每個用戶對數據庫emp表進行數據操縱(插入、更新、刪除)的次數紀錄到audit_table表中
CREATE OR REPLACE TRIGGER audit_emp
AFTER DELETE OR INSERT OR UPDATE ON emp FOR EACH ROW
BEGIN
IF DELETING THEN
UPDATE audit_table SET del = del + 1
WHERE user_name = user AND table_name = 'emp'
AND colun_name IS NULL;
ELSIF INSERTING THEN
UPDATE audit_table SET ins = ins + 1
WHERE user_name = user AND table_name = 'emp'
AND colun_name IS NULL;
ELSE THEN
UPDATE audit_table SET upd = upd + 1
WHERE user_name = user AND table_name = 'emp'
AND colun_name IS NULL;
END IF;
END; ????
?
?
使用行級觸發器的標識符(:OLD和:NEW)
在行級觸發器中,列名前加上:OLD標識符表示該列變化前的值,加上:NEW標識符表示變化后的值
在BEFORE型行級觸發器和AFTER型行級觸發器中使用這些標識符
在語句級觸發器中不要使用這些標識符
在觸發器體的SQL語句或PL/SQL語句中使用這些標識符時,前加冒號(:)
在行級觸發器的WHEN限制條件中使用這些標識符時,前面不要加冒號(:)
?
Eg:在行級觸發器中獲取某列的新值和舊值,為emp表中的所有數據保留一個歷史檔案
CREATE OR REPLACE TRIGGER audit_emp_values
AFTER DELETE OR INSERT OR UPDATE ON emp FOR EACH ROW
BEGIN
INSERT INTO audit_emp(user_name,timestamp,empno,
old_ename,new_ename,old_job,new_job,
old_sal,new_sal)
VALUES(USER,SYSDATE,:old.empno,:old.ename,
:new.ename,:old.job,:new.job,:old.sal,:newsal);
END;
?
?
Eg:在行級觸發器加WHEN限制條件。根據銷售員工資的改變自動計算銷售員的獎金
CREATE OR REPLACE TRIGGER derive_comm
BEFORE UPDATE OF sal ON emp FOR EACH ROW
WHEN (new.job = 'SALESMAN')
BEGIN
:new.comm := :old.comm * (:new.sal/:old.sal);
END;????????
?
?
CREATE TIGGER
CREATE OR REPLACE TRIGGER
DROP TRIGGER
?
SYSTEM觸發器
DDL觸發器
?
INSTEAD OF 觸發器
instead of trigger 是基于視圖建立的,不能建在表上,為什么要建在視圖上,一般的視圖如果其數據來源一個表并且包含該表的主鍵,就可以對視圖進行DML操作.另外一種情況是 從多個表查詢出來的.這樣我們就不能對視圖進行操作了,也就是只能查詢.instead of trigger可以解決建在多表上視圖的更新操作.
?
?
?
?
BULK DML
Oracle引入了兩個DML語句: BUKL COLLECT 和 FORALL
這兩個語句在PL/SQL內部進行以一種數據處理
BUKL COLLECT 提供對數據的高速檢索
FORALL 可大大改進INSERT,UPDATE和DELETE操作的性能
Oracle數據庫使用這些語句大大減少了PL/SQL與SQL語句執行引擎的環境切換次數,從而使其性能有了顯著提高
?
DECLARE
????TYPE books_aat IS TABLE OF book%ROWTYPE
????????INDEX BY PLS_INTEGER;
????my_books books_aat;
BEGIN
????SELECT * BULK COLLECT INTO my_books
????FROM book WHERE title LIKE '%PL/SQL%';
????...
END;
?
?
CREATE TYPE books_nt
IS TABLE OF book%ROWTYPE;
CREATE OR REPLACE PROCEDURE add_books ( books_in IN books_nt)
IS
BEGIN
????FORALL book_index
????????IN books_in.FIRST .. books_in.LAST
????INSERT INTO book VALUES books_in(book_index);
????...
END;
?
?
?
?
DECLARE
Type region_id_tbl IS TABLE of NUMBER INDEX BY BINARY_INTEGER;
Type region_name_tbl IS TABLE of VARCHAR2(20) INDEX BY BINARY_INTEGER;
region_ids region_id_tbl; region_names region_name_tbl; ret_code NUMBER;
ret_errmsg VARCHAR2(1000);
Procedure load_regions_bulk_bind (region_ids IN region_id_tbl, region_names IN region_name_tbl,
retcd OUT NUMBER, errmsg OUT VARCHAR2)
IS
BEGIN
-- clean up the region_tab table initially.
DELETE FROM region_tab;
FORALL i IN region_ids.FIRST..region_ids.LAST
INSERT INTO region_tab values (region_ids(i), region_names(i));
Retcd := 0;
EXCEPTION
WHEN OTHERS THEN
COMMIT;
Retcd := SQLCODE;
Errmsg := SQLERRM;
END;
BEGIN
FOR i IN 1..5 LOOP
Region_ids(i) := i; Region_names(i) := 'REGION'||i;
END LOOP;
Load_regions_bulk_bind(region_ids, region_names, ret_code, ret_errmsg);
EXCEPTION WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20112, SQLERRM);
END;
?
?
?
什么時候使用FORALL
FORALL可以大量的提高從源表或者視圖到目標表的插入,更新,刪除,合并操作
?
如果FORALL的調用集合中缺失一個元素那么錯誤將會發生
如果一個行級別的發生那么整個進程將會停止
?
?
DECLARE
t_outtab_type IS TABLE OF input_table%ROWTYPE;
v_outtab t_outtab_type;
BEGIN
SELECT * BULK COLLECT INTO v_outtab
FROM input_table
WHERE key_col = '<val1>';
?
FOR i IN 1..v_outtab.COUNT LOOP
-- Process the rows one by one according to required logic.
END LOOP;
END;
/
?
DECLARE
t_outtab_type IS TABLE OF input_table%ROWTYPE;
v_outtab t_outtab_type;
CURSOR c_in IS SELECT * FROM input_table
WHERE key_col = '<val1>';
BEGIN
OPEN c_in;
FETCH c_in BULK COLLECT INTO v_outtab;
FOR i IN 1..v_outtab.COUNT LOOP
-- Process the rows one by one according to required logic.
END LOOP;
?
CLOSE c_in;
END;
/
?
DECLARE
t_outtab_type IS TABLE OF input_table%ROWTYPE;
v_outtab t_outtab_type;
CURSOR c_in IS SELECT * FROM input_table
WHERE key_col = '<val1>';
BEGIN
OPEN c_in;
FETCH c_in BULK COLLECT INTO v_outtab;
FOR i IN 1..v_outtab.COUNT LOOP
-- Process the rows one by one according to required logic.
END LOOP;
?
CLOSE c_in;
END;
/
?
?
用bulk query的主要有點,
減少運行的時間,更少的延時
如果取得的數據很大的話,那么回增加內存的消耗
?
可以通過特定的limite數量來控制取得的數量
DECLARE
t_outtab_type IS TABLE OF input_table%ROWTYPE;
v_outtab t_outtab_type;
CURSOR c_in IS SELECT * FROM input_table WHERE key_col = '<val1>';
BEGIN
OPEN c_in;
LOOP
FETCH c_in BULK COLLECT INTO v_outtab LIMIT 100;
FOR i IN 1..v_outtab.COUNT LOOP
-- Process the rows one by one according to required logic.
NULL;
END LOOP;
EXIT WHEN c_in%NOTFOUND;
END LOOP;
CLOSE c_in;
END;
/
?
自治事物
自治事物允許你在一個事務中創建一個事務,這個事務將會獨立于父事務進行提交,回滾等改變
允許你掛起現在正在執行的事務,開始一個新的事務,做一些工作,提交回滾,所有的這些事情不會影響當前正在執行事務的狀態
通過pragma autonomous_transaction將一個pl/sql程序結構設定為自治事務,pragma是編譯器指令,可以將procedure function package等頂級匿名塊定義成自治的程序結構。
?
create or replace trigger EMP_AUDIT
????before update on emp for each row
declare
????pragma autonomous_transaction;
????l_cnt number;
begin
????select count(*) into l_cnt from dual
????where EXISTS ( select null from emp where empno = :new.empno
????????start with mgr = ( select empno from emp where ename = USER )
????????connect by prior empno = mgr );
????if ( l_cnt = 0 ) then
????????insert into audit_tab ( msg )
????????values ( 'Attempt to update ' || :new.empno );
????????commit;
????????raise_application_error( -20001, 'Access Denied' );
????end if;
end;
/
?
?
?
?
?
綁定變量
下面的代碼每次都要硬編碼
create or replace procedure dsal(p_empno in number)
as
begin
????execute immediate
????????'update emp set sal = sal*2
????????where empno = '||p_empno;
????commit;
end;
?
通過如下改變就可以了
create or replace procedure dsal(p_empno in number)
as
begin
????execute immediate
????????'update emp set sal = sal*2
????????where empno = :x' using p_empno;
????commit;
end;
?
游標變量 : 聲明游標實際上是創建一個指針 , 指針具有數據類型 REF X.REF 是 REFERENCE ,X 是表示類對象 . 因此 , 游標變量具有數據類型 REF? CURSOR.
注 : 游標總是指向相同的查詢工作區 , 游標變量能夠指向不同的工作區 , 因此游標和游標變量不能互操作 .
DECLARE
TYPE empcurtyp IS REF CURSOR RETURN employees%ROWTYPE;
emp empcurtyp;
-- after result set is built, process all the rows inside a single procedure
-- rather than calling a procedure for each row
PROCEDURE process_emp_cv (emp_cv IN empcurtyp)
IS
person employees%ROWTYPE;
BEGIN
DBMS_OUTPUT.PUT_LINE('-----');
DBMS_OUTPUT.PUT_LINE('Here are the names from the result set:');
LOOP
FETCH emp_cv INTO person;
EXIT WHEN emp_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Name = ' || person.first_name || ' ' || person.last_name);
END LOOP;
END;
BEGIN
-- First find 10 arbitrary employees.
OPEN emp FOR SELECT * FROM employees WHERE ROWNUM < 11;
process_emp_cv(emp);
CLOSE emp;
?
-- find employees matching a condition.
OPEN emp FOR SELECT * FROM employees WHERE last_name LIKE 'R%';
process_emp_cv(emp);
CLOSE emp;
END;
轉載于:https://www.cnblogs.com/kexinxin/p/10011934.html
總結
- 上一篇: .net core中使用GB2312编码
- 下一篇: JAVA-WEB开发环境和搭建