PL/SQL(Procedure Language/Structured Query Language)
一、PL/SQL是一種高級數據庫程序設計語言,專門用於在各類環境下對Oracle數據庫進行訪問。該語言集成於數據庫服務器中,因此PL/SQL代碼能夠對數據進行快速高效的處理。
二、PL/SQL是對SQL語言存儲過程語言的擴展,是Oracle系統的核心語言。
三、PL/SQL程序由三個塊組成:聲明部分、執行部分、異常處理部分。
先去Oracle官網去下載最新版本的sqldeveloper,下載地址:https://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html
獲得2個zip壓縮包,以下圖所示:html
小案例-回顧條件表達式:java
給員工漲工資:總裁漲1000元 經理漲800元 其餘漲400元
寫一段java的JDBC程序,咱們這裏寫的是僞代碼,僞代碼不可以執行,可是能夠幫助咱們分析程序執行的過程和結構。
ResultSet rs = "select empno,job from emp";
while(rs.next()) {
int eno = rs.getInt("empno");
String job = rs.getString("job");
if("PRESIDENT".eauals(job)) {
update emp sal=sal+1000 where empno=eno;
} else if ("MANAGER".eauals(job)) {
update emp sal=sal+800 where empno=eno;
} else {
update emp sal=sal+400 where empno=eno;
}
PL/SQL = Procedure Language/SQL = 過程語言/SQL
PL/SQL程序從功能上來說,與上面JDBC的程序想要完成的功能是同樣的。
學習PL/SQL程序的目的:
一、PL/SQL是Oracle對SQL語言的過程化擴展,操做效率更高。
二、PL/SQL在SQL命令語言中增長了過程處理語句(分支、循環等),使SQL語言具備過程處理能力。
咱們把SQL語言的數據操縱能力與過程語言的數據處理能力結合起來,使得PL/SQL面向過程但比過程語言簡單、高效、靈活和實用。
Oracle中對SQL語言的擴展叫作PL/SQL。
SQL Server中對SQL語言的擴展叫作Transact-sql。sql
示例代碼以下:shell
SQL> --聲明部分
SQL> declare
2 --說明部分
3 begin
4 --程序部分
5 dbms_output.put_line('Hello World');
6 end;
7 --退出編輯環境,並執行PL/SQL程序
8 /
PL/SQL 過程已成功完成。
SQL> --默認狀況下,Oracle的輸出開關是關閉的。
SQL> --若是要在屏幕上輸出信息,須要將 serveroutput開關打開 set serveroutput on
SQL> set serveroutput on
SQL> /
Hello World
PL/SQL 過程已成功完成。
SQL>
PL/SQL程序結構截圖以下:數據庫
--查詢員工編號爲7839的姓名和薪水
set serveroutput on
declare
--定義變量保存姓名和薪水
--pename varchar2(20);
--psal number;
--定義引用型變量保存姓名和薪水
pename emp.ename%type;
psal emp.sal%type;
begin
--獲得姓名和薪水
--在PL/SQL中,賦值方式有兩種方式,一種是 := 一種是 使用關鍵字into
select ename,sal into pename,psal from emp where empno=7839;
dbms_output.put_line(pename||'的薪水是'||psal);
end;
/
記錄型變量示例代碼:安全
--查詢員工編號爲7839的姓名和薪水
set serveroutput on
declare
--定義記錄型變量:表明一行
emp_rec emp%rowtype;
begin
select * into emp_rec from emp where empno=7839;
dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);
end;
/
如何定義常量呢?性能優化
pename emp.ename%type;
psal emp.sal%type;
加一個constant,就變成常量了。
pename constant emp.ename%type;
psal constant emp.sal%type;
set serveroutput on
--判斷用戶從鍵盤輸入的數字
--接收鍵盤輸入
--num: 地址值,在該地址上保存了輸入的值。
accept num prompt '請輸入一個數字';
declare
--定義變量保存輸入的數字
pnum number := #
begin
if pnum = 0 then dbms_output.put_line('您輸入的是0');
elsif pnum = 1 then dbms_output.put_line('您輸入的是1');
elsif pnum = 2 then dbms_output.put_line('您輸入的是2');
else dbms_output.put_line('其餘數字');
end if;
end;
/
--打印1~10
set serveroutput on
declare
pnum number := 1;
begin
loop
--退出條件
exit when pnum > 10;
dbms_output.put_line(pnum);
--加一
pnum := pnum + 1;
end loop;
end;
/
示例:按員工的工種長工資,總裁漲1000元,經理漲800元,其餘員工漲400元。
示例代碼截圖:bash
--查詢並打印員工的姓名和薪水
/*
1. 光標的屬性:
%isopen(光標是否打開)
%rowcount(光標影響的行數)
%found(光標找到內容)
%notfound(光標沒有找到內容)
2. Oracle中默認,一個會話中只能打開300個光標
SQL> --修改光標個數須要管理員權限
SQL> show user
USER 爲 "SCOTT"
SQL> conn sys/password@192.168.56.101:1521/orcl as sysdba
已鏈接。
SQL> show user
USER 爲 "USER"
SQL> show parameter cursor
NAME TYPE VALUE
------------------------------------ -------------------------------- -----------
cursor_sharing string FORCE
cursor_space_for_time boolean FALSE
open_cursors integer 300
session_cached_cursors integer 20
修改: alter system set open_cursors=400;
3. (思考):上面參數 cursor_sharing 什麼做用? --> 對於數據庫性能優化很是有用。
EXACT(默認值), FORCE(應急使用), SIMILAR
*/
--示例:使用光標查詢員工姓名和工資,並打印
set serveroutput on
declare
--定義一個光標
cursor cemp is select ename,sal from emp;
--爲這個光標定義所須要用到的對應的變量
pename emp.ename%type;
psal emp.sal%type;
begin
--打開光標
open cemp;
loop
--取一條記錄到變量中
fetch cemp into pename,psal;
--退出條件
--exit when 沒有取到記錄;
exit when cemp%notfound;
--打印
dbms_output.put_line(pename||'的薪水是'||psal);
end loop;
--關閉光標
close cemp;
end;
/
再來給員工漲工資代碼:服務器
--示例:按員工的工種長工資,總裁漲1000元,經理漲800元,其餘員工漲400元。
set serveroutput on
declare
--alter table "SCOTT"."EMP" rename column "JOB" to empjob
cursor cemp is select empno,empjob from emp;
--爲這個光標定義所須要用到的對應的變量
pempno emp.empno%type;
pjob emp.empjob%type;
begin
open cemp;
loop
--取一條記錄到變量中
fetch cemp into pempno,pjob;
exit when cemp%notfound;
--判斷職位
if pjob = 'PRESIDENT' then update emp set sal=sal+1000 where empno=pempno;
elsif pjob = 'MANAGER' then update emp set sal=sal+800 where empno=pempno;
else update emp set sal=sal+400 where empno=pempno;
end if;
end loop;
close cemp;
--Oracle是自動開啓事務的
--Oracle默認的隔離級別是:read committed
--why? --> ACID
commit;
dbms_output.put_line('漲工資完成');
end;
/
帶參數的光標
示例代碼以下:session
--查詢某個部門的員工姓名
set serveroutput on
declare
cursor cemp(dno number) is select ename from emp where deptno=dno; --不同的地方
pename emp.ename%type;
begin
open cemp(20); --不同的地方
loop
fetch cemp into pename;
exit when cemp%notfound;
dbms_output.put_line(pename);
end loop;
close cemp;
end;
/
例外:是程序設計語言提供的一種功能,用來加強程序的健壯性和容錯性。
Oracle中對異常的處理
一、系統定義的例外
No_data_found (沒有找到數據)
Too_many_rows (select … into 語句中匹配多個行)
Zero_Divide (被零除)
Value_error (算術或轉換錯誤)
Timeout_on_resource (在等待資源時發生超時)
二、用戶定義的例外
演示:系統定義的例外(被0除)
--系統例外:被0除
set serveroutput on
declare
pnum number;
begin
pnum := 1/0;
exception
when zero_divide then dbms_output.put_line('1:0不能作分母');
dbms_output.put_line('2:0不能作分母');
when value_error then dbms_output.put_line('算術或轉換錯誤');
when others then dbms_output.put_line('其餘例外');
end;
/
演示:用戶定義的例外以及處理例外
--查詢50號部門的員工姓名
set serveroutput on
declare
cursor cemp is select ename from emp where deptno=50;
pename emp.ename%type;
--自定義例外
no_emp_found exception;
begin
open cemp;
--取第一條記錄
fetch cemp into pename;
if cemp%notfound then
--拋出例外
raise no_emp_found;
end if;
--回顧
--Java中是經過IO流來操做硬盤中的文件,
--Java中IO最終是經過什麼方式操做硬盤上的文件呢?答:經過操做系統的進程。
--Oracle中經過內存中的實例操做硬盤中的文件,
--而內存中實例最終是怎麼操做硬盤上的文件呢?答:也是經過操做系統的進程。
--這句執行不到,Oracle中怎麼辦呢?答:經過進程監視器
--pmon: process monitor 進程監視器
close cemp;
exception
when no_emp_found then dbms_output.put_line('沒有找到員工');
when others then dbms_output.put_line('其餘例外');
end;
/
瀑布模型圖解:
/*
SQL語句:
select to_char(hiredate,'yyyy') from emp;
--> 集合 --> 光標 --> 循環 --> 退出條件:notfound
變量:
1. 初始值
2. 最終怎麼獲得
每一年入職的員工人數:
count80 number := 0;
count81 number := 0;
count82 number := 0;
count87 number := 0;
*/
set serveroutput on
declare
cursor cemp is select to_char(hiredate,'yyyy') from emp;
phiredate varchar2(4);
--每一年入職的員工人數:
count80 number := 0;
count81 number := 0;
count82 number := 0;
count87 number := 0;
begin
open cemp;
loop
--取一個員工的入職年份到變量中
fetch cemp into phiredate;
--退出條件:notfound
exit when cemp%notfound;
--判斷年份
if phiredate = '1980' then count80:=count80+1;
elsif phiredate = '1981' then count81:=count81+1;
elsif phiredate = '1982' then count82:=count82+1;
else count87:=count87+1;
end if;
end loop;
close cemp;
dbms_output.put_line('Total:'||(count80+count81+count82+count87));
dbms_output.put_line('1980年入職的有:'||count80);
dbms_output.put_line('1981年入職的有:'||count81);
dbms_output.put_line('1982年入職的有:'||count82);
dbms_output.put_line('1987年入職的有:'||count87);
end;
/
實例2:爲員工漲工資,從最低工資調起每人漲10%,但工資總額不能超過5萬元,請計算漲工資的人數和漲工資後的工資總額,並輸出漲工資人數及工資總額。
/*
SQL語句:
select empno,sal from emp order by sal;
--> 光標 --> 退出條件:1. 工資總額 > 5w 2. notfound
變量:
1. 初始值
2. 最終獲得
漲工資的人數: countEmp number := 0;
漲後的工資總額: salTotal number;
方式1. select sum(sal) into salTotal from emp;
方式2. 漲後=漲前 + sal * 0.1
寫程序的原則:能不操做數據庫就不要操做數據庫。
練習:人數:7 總額:50205.325
*/
set serveroutput on
declare
cursor cemp is select empno,sal from emp order by sal;
pempno emp.empno%type;
psal emp.sal%type;
--漲工資的人數:
countEmp number := 0;
--漲後的工資總額:
salTotal number;
begin
--獲得初始的工資總額
select sum(sal) into salTotal from emp;
open cemp;
loop
--取一個員工出來到變量中
fetch cemp into pempno,psal;
--1. 工資總額 > 5w
exit when salTotal > 50000;
--2. notfound
exit when cemp%notfound;
--漲工資操做
update emp set sal=sal*1.1 where empno=pempno;
--人數+1
countEmp := countEmp + 1;
--2. 漲後工資總額=漲前工資總額 + sal * 0.1
salTotal := salTotal + psal * 0.1;
end loop;
close cemp;
commit;
dbms_output.put_line('人數:'||countEmp||' 總額:'||salTotal);
end;
/
實例3:用PL/SQL語言編寫一程序,實現按部門分段(6000以上、(6000,3000)、3000元如下)統計各工資段的職工人數、以及各部門的工資總額(工資總額中不包括獎金)
/*
SQL語句:
部門: select deptno from dept;
部門中員工的薪水:select sal from emp where deptno=???; 問號是部門編號
變量:
1. 初始值
2. 最終獲得
每一個段的人數:
count1 number;
count2 number;
count3 number;
部門的工資總額:
salTotal number := 0;
獲得部門的工資總額的方式:
1.select sum(sal) into salTotal from emp where deptno=???;
2.累加
*/
set serveroutput on
declare
--部門
cursor cdept is select deptno from dept;
pdeptno dept.deptno%type;
--部門中員工的薪水
cursor cemp(dno number) is select sal from emp where deptno=dno;
psal emp.sal%type;
--每一個段的人數:
count1 number;
count2 number;
count3 number;
--部門的工資總額:
salTotal number := 0;
begin
open cdept;
loop
--取一個部門
fetch cdept into pdeptno;
exit when cdept%notfound;
--初始化
--每一個段的人數
count1:=0;
count2:=0;
count3:=0;
--獲得部門的工資總額
select sum(sal) into salTotal from emp where deptno=pdeptno;
--取部門中員工的薪水
open cemp(pdeptno);
loop
--取一個員工
fetch cemp into psal;
exit when cemp%notfound;
--判斷
if psal < 3000 then count1:=count1+1;
elsif psal>=3000 and psal<6000 then count2:=count2+1;
else count3:=count3+1;
end if;
end loop;
close cemp;
--保存結果
insert into msg values(pdeptno,count1,count2,count3,nvl(saltotal,0));
end loop;
close cdept;
commit;
dbms_output.put_line('完成');
end;
/
筆試1腳本.txt
create table test1
(id int primary key,
name varchar(20),
money int);
insert into test1 values(1,'Tom',1000);
insert into test1 values(2,'Mary',2000);
insert into test1 values(3,'Mike',3000);
insert into test1 values(4,'Jeff',4000);
commit;
示例代碼以下:
SQL> select * from test1;
ID NAME MONEY
---------- -------------------- ----------
1 Tom 1000
2 Mary 2000
3 Mike 3000
4 Jeff 4000
SQL> select id,name,money,(select money from test1 where id=t.id-1) money1 from test1 t;
ID NAME MONEY MONEY1
---------- -------------------- ---------- ----------
1 Tom 1000
2 Mary 2000 1000
3 Mike 3000 2000
4 Jeff 4000 3000
SQL>
筆試2腳本.txt
create table pm_ci
(ci_id varchar(20) primary key,
stu_ids varchar(100));
insert into pm_ci values('1','1,2,3,4');
insert into pm_ci values('2','1,4');
create table pm_stu
(stu_id varchar(20) primary key,
stu_name varchar(20));
insert into pm_stu values('1','張三');
insert into pm_stu values('2','李四');
insert into pm_stu values('3','王五');
insert into pm_stu values('4','趙六');
commit;
示例代碼以下:
SQL> select * from pm_ci;
CI_ID STU_IDS
-------------------- ----------------------------------------------------------------------------------------------------
1 1,2,3,4
2 1,4
SQL> select * from pm_stu;
STU_ID STU_NAME
-------------------- --------------------
1 張三
2 李四
3 王五
4 趙六
SQL> select c.ci_id,s.stu_name
2 from pm_ci c,pm_stu s
3 where instr(c.stu_ids,s.stu_id)>0;
CI_ID STU_NAME
-------------------- --------------------
1 張三
1 李四
1 王五
1 趙六
2 張三
2 趙六
已選擇 6 行。
SQL> select ci_id,wm_concat(stu_name) namelist
2 from(select c.ci_id,s.stu_name
3 from pm_ci c,pm_stu s
4 where instr(c.stu_ids,s.stu_id)>0)
5 group by ci_id;
CI_ID
--------------------
NAMELIST
------------------------------------------------------------------------------------------------------------------------------------------------------
1
張三,李四,王五,趙六
2
張三,趙六
SQL> --設置列的寬度
SQL> col namelist for a50
SQL> select ci_id,wm_concat(stu_name) namelist
2 from(select c.ci_id,s.stu_name
3 from pm_ci c,pm_stu s
4 where instr(c.stu_ids,s.stu_id)>0)
5 group by ci_id;
CI_ID NAMELIST
-------------------- --------------------------------------------------
1 張三,李四,王五,趙六
2 張三,趙六
SQL>
詳解以下:
存儲在數據庫中供全部用戶程序調用的子程序(用PL/SQL寫的)叫存儲過程、存儲函數。
建立存儲過程的語法:
create [or replace] PROCEDURE 過程名(參數列表)
as PL/SQL子程序體;
示例代碼1:
--打印Hello World,不傳遞參數
/*
調用存儲過程的方式:
1. exec sayHelloWorld();
2. begin
sayHelloWorld();
sayHelloWorld();
sayHelloWorld();
end;
/
*/
create or replace procedure sayHelloWorld --注意Oracle中的命名規範,可是這裏爲了簡便,咱們使用java的命名規範
as
--說明部分
begin
dbms_output.put_line('Hello World');
end;
/
示例代碼2:
--給指定的員工漲100,而且打印漲前和漲後的工資,傳遞單個參數
create or replace procedure raisesalary(eno in number) --注意:須要指明參數是輸入參數,仍是輸出參數
as
--定義變量保存漲前的薪水
psal emp.sal%type;
begin
--獲得漲前的薪水
select sal into psal from emp where empno=eno;
--漲100
update emp set sal=sal+100 where empno=eno;
--要不要commit呢?答:不要。
--原則:通常狀況下,咱們不在存儲過程和存儲函數中commit和rollback數據,應該交由調用者去作。
dbms_output.put_line('漲前:'||psal||' 漲後:'||(psal+100));
end;
/
示例代碼3:
--給指定的員工漲指定額度的工資,傳遞多個參數
create or replace procedure raiseSalary(eno in number,rate in number)
as
psal emp.sal%type;
begin
--獲得漲前的薪水
select sal into psal from emp where empno=eno;
--漲指定額度的工資
update emp set sal=sal*rate where empno=eno;
dbms_output.put_line('漲前:'||psal||' 漲後:'||(psal*rate));
end;
詳解以下:
存儲函數和存儲過程的結構相似,但必須有一個return子句,用於返回函數值。
函數說明要指定函數名、結果值的類型,以及參數類型等。
建立存儲函數的語法:
create [or replace] FUNCTION 函數名(參數列表)
return 函數返回值類型
as PL/SQL子程序體;
示例代碼1:
--查詢某個員工的年收入
create or replace function queryempincome(eno in number)
return number
as
--定義變量保存月薪和獎金
psal emp.sal%type;
pcomm emp.comm%type;
begin
select sal,comm into psal,pcomm from emp where empno=eno;
--返回年收入
return psal*12+nvl(pcomm,0);
end;
/
詳解以下:
通常來說,存儲過程和存儲函數區別在於存儲函數能夠有一個返回值,而存儲過程沒有返回值。
但存儲過程和存儲函數均可以經過out指定一個或多個輸出參數。咱們能夠利用out參數,在存儲過程和存儲函數中實現返回多個值。
這時存儲函數的功能就被存儲過程取代了,那爲何還要保留存儲函數呢?答:爲了版本的向下兼容。
何時使用存儲過程/存儲函數呢?
原則:
通常而言,若是隻有一個返回值,就用存儲函數;不然,就用存儲過程。
示例代碼以下:
--查詢某個員工的姓名 月薪 職位
create or replace procedure queryempinfo(eno in number,
pename out varchar2,
psal out number,
pjob out varchar2)
as
begin
select ename,sal,empjob into pename,psal,pjob from emp where empno=eno;
end;
/
思考:
1. 查詢某個員工的全部信息 --> 問題:out參數太多
2. 查詢某個部門中的全部員工信息 --> 問題:返回的是集合
在Java中調用存儲過程和存儲函數 的示例代碼:
/*
create or replace procedure queryempinfo(eno in number,
pename out varchar2,
psal out number,
pjob out varchar2)
as
begin
select ename,sal,empjob into pename,psal,pjob from emp where empno=eno;
end;
*/
@Test
public void testProcedure() {
// {call <procedure-name>[(<arg1>,<arg2>, ...)]}
String sql = "{call queryempinfo(?,?,?,?)}";
Connection conn = null;
CallableStatement call = null;
try {
conn = JDBCUtils.getConnection();
call = conn.prepareCall(sql);
// 對於in參數,須要賦值
call.setInt(1, 7839);
// 對於out參數,須要聲明
call.registerOutParameter(2, OracleTypes.VARCHAR);
call.registerOutParameter(3, OracleTypes.NUMBER);
call.registerOutParameter(4, OracleTypes.VARCHAR);
// 執行存儲過程
call.execute();
// 取出結果
String name = call.getString(2);
double sal = call.getDouble(3);
String job = call.getString(4);
System.out.println(name + "\t" + sal + "\t" + job);
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.release(conn, call, null);
}
}
/*
create or replace function queryempincome(eno in number)
return number
as
--定義變量保存月薪和獎金
psal emp.sal%type;
pcomm emp.comm%type;
begin
select sal,comm into psal,pcomm from emp where empno=eno;
--返回年收入
return psal*12+nvl(pcomm,0);
end;
*/
@Test
public void testFunction(){
// {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
String sql = "{?=call queryempincome(?)}";
Connection conn = null;
CallableStatement call = null;
try {
conn = JDBCUtils.getConnection();
call = conn.prepareCall(sql);
// 第一個是out參數,須要聲明
call.registerOutParameter(1, OracleTypes.NUMBER);
// 第二個是in參數,須要賦值
call.setInt(2, 7839);
// 執行存儲函數
call.execute();
// 取出年收入
double income = call.getDouble(1);
System.out.println(income);
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.release(conn, call, null);
}
}
查詢某個部門中全部員工的全部信息,返回的是集合。
咱們須要聲明包結構和建立包體,其中包和包體也是數據庫的對象。
示例代碼以下:
/*
1. 查詢某個員工的全部信息 --> 問題:out參數太多
2. 查詢某個部門中的全部員工信息 --> 問題:返回的是集合
*/
// 在out參數中使用光標
// 查詢某個部門中全部員工的全部信息
/*
--聲明包結構
create or replace
package myPackage as
type empcursor is ref cursor;
procedure queryEmpList(dno in number,emplist out empcursor);
end myPackage;
--建立包體
create or replace
package body myPackage as
procedure queryEmpList(dno in number,emplist out empcursor) as
begin
open emplist for select * from emp where deptno=dno;
end queryEmpList;
end myPackage;
*/
@Test
public void testCursor(){
String sql = "{call myPackage.queryEmpList(?,?)}";
Connection conn = null;
CallableStatement call = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
call = conn.prepareCall(sql);
// 對於in參數,須要賦值
call.setInt(1, 20);
// 對於out參數 ,須要聲明
call.registerOutParameter(2, OracleTypes.CURSOR);
// 執行存儲函數
call.execute();
// 取出結果
rs = ((OracleCallableStatement)call).getCursor(2);
while (rs.next()) {
// 取出一個員工,示例只取出了兩列
String name = rs.getString("ename");
double sal = rs.getDouble("sal");
System.out.println(name + "\t" + sal);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.release(conn, call, rs);
}
}
詳解以下:
數據庫觸發器是一個與表相關聯的、存儲的PL/SQL程序。
每當一個特定的數據操做語句(insert、update、delete)在指定的表上發出時,Oracle自動地執行觸發器中定義的語句序列。
觸發器的類型:
語句級(表級)觸發器:在指定的操做語句操做以前或以後執行一次,無論這條語句影響了多上行。
行級觸發器(for each row):觸發語句做用的每一條記錄都被觸發。在行級觸發器中使用 :old 和 :new 僞記錄變量來識別值的狀態。
建立觸發器的語法:
create or replace trigger 觸發器名
before | after
insert | update | delete [of 列名]
on 表名
[for each row [when(條件)]] --觸發器的類型
declare
begin
......
end;
觸發器的用途:
1. 數據確認
2. 實施複雜的安全性檢查
3. 作審計,跟蹤表上所作的數據操做等(想要作什麼事,不被查到,須要關閉數據庫的審計功能)
4. 數據的備份和同步
示例1:
--每當成功插入新員工後,自動打印「成功插入了新員工」
create or replace trigger abcd
after insert
on emp
declare
begin
dbms_output.put_line('成功插入了新員工');
end;
禁止在非工做時間向數據庫中插入數據
週末:to_char(sysdate,'day') in ('星期六','星期日')
上班前 下班後:to_number(tochar(sysdate,'hh24')) not between 9 and 17
------------------------------------------------------------------
create or replace trigger securityemp
before insert
on emp
declare
begin
if to_char(sysdate,'day') in ('星期六','星期日') or
to_number(to_char(sysdate,'hh24')) not between 9 and 17 then
--禁止insert
raise_application_error(-20002,'禁止在非工做時間向數據庫中插入數據'); -- -20000到-20999之間
end if;
end;
------------------------------------------------------------------
SQL> insert into emp(empno,ename,sal,deptno) values(1001, 'tom',3000, 20);
insert into emp(empno,ename,sal,deptno) values(1001, 'tom',3000, 20)
*
第 1 行出現錯誤:
ORA-20002: 禁止在非工做時間向數據庫中插入數據
ORA-06512: 在 "SCOTT.SECURITYEMP", line 6
ORA-04088: 觸發器 'SCOTT.SECURITYEMP' 執行過程當中出錯
SQL>
檢查emp表中的sal的修改值不低於原值
------------------------------------------------------------------
create or replace trigger checksalary
before update
on emp
for each row
declare
begin
if :new.sal<:old.sal then
raise_application_error(-20001,'漲後的工資不能少於漲前的工資。漲前:'||:old.sal||' 漲後:'||:new.sal); -- -20000到-20999之間
end if;
end;
------------------------------------------------------------------
測試代碼:
SQL> update emp set sal=sal+1 where empno=7839;
已更新 1 行。
SQL> update emp set sal=sal-1 where empno=7839;
update emp set sal=sal-1 where empno=7839
*
第 1 行出現錯誤:
ORA-20001: 漲後的工資不能少於漲前的工資。漲前:7987 漲後:7986
ORA-06512: 在 "SCOTT.CHECKSALARY", line 4
ORA-04088: 觸發器 'SCOTT.CHECKSALARY' 執行過程當中出錯
SQL>
限制每一個部門只招聘10名員工,超過計劃則報出錯誤信息
------------------------------------------------------------------
create or replace trigger limitEmpCount
before insert
on emp
declare
count10 number := 0;
count20 number := 0;
count30 number := 0;
begin
select count(*) into count10 from emp where deptno=10;
select count(*) into count20 from emp where deptno=20;
select count(*) into count30 from emp where deptno=30;
if count10>=10 then raise_application_error(-20005,'部門:10,員工已有'||count10||'人');
elsif count20>=10 then raise_application_error(-20005,'部門:20,員工已有'||count20||'人');
elsif count30>=10 then raise_application_error(-20005,'部門:30,員工已有'||count30||'人');
end if;
end;
------------------------------------------------------------------
測試代碼:
SQL> insert into emp(empno,ename,sal,deptno) values(1030,'tom',3000, 30);
insert into emp(empno,ename,sal,deptno) values(1030,'tom',3000, 30) *第 1 行出現錯誤:ORA-20005: 部門:30,員工已有10人ORA-06512: 在 "SCOTT.LIMITEMPCOUNT", line 12ORA-04088: 觸發器 'SCOTT.LIMITEMPCOUNT' 執行過程當中出錯SQL>