PL/SQL 結構
DECLARE --聲明部分
聲明語句
BEGIN --執行部分
執行語句
EXCEPTION --異常處理部分
執行語句
END;
變量聲明
<變量名> 類型[:=初始值];
特殊類型 字段%type
示例: name emp.ename%type –表示name的類型和emp.ename的類型相同
表 %rowtype
示例:
test emp%rowtype –表示test的類型爲emp表的行類型;也有 .empno; .ename; .sal ;等屬性
常量聲明
<變量名> CONSTANT 類型:=初始值; 示例: pi constant number(5,3):=3.14;
全局變量聲明
VARIABLE <變量名> 類型;
示例: VARIABLE num number;
使用全局變量
:<變量名>
示例:
:num:=100;
i=:num;
查看全局變量的值
print <變量名>
示例: print num;
賦值運算符: :=
示例: num := 100;
使用SELECT <列名> INTO <變量名> FROM <表名> WHERE <條件>
注意select into 語句的返回結果只能爲一行;
示例:test emp%rowtype;
select * into test from emp where empno=7788;
用戶交互輸入
<變量>:=’&變量’
示例:
num:=#
注意oracle的用戶交互輸入是先接受用戶輸入的所有值後在執行語句;
所以不能使用循環進行用戶交互輸入;
條件控制語句
IF <條件1> THEN
語句
[ELSIF <條件2> THEN
語句
.
.
.
ELSIF <條件n> THEN
語句]
[ELSE
語句]
END IF;
循環控制語句
1.LOOP
LOOP
語句;
EXIT WHEN <條件>
END LOOP;
2.WHILE LOOP
WHILE <條件>
LOOP
語句;
END LOOP;
3.FOR
FOR <循環變量> IN 下限..上限
LOOP
語句;
END LOOP;
NULL 語句
null;
表示沒有操作;
註釋使用
單行註釋: –
多行註釋:/* …….
……………*/
異常處理
EXCEPTION
WHEN <異常類型> THEN
語句;
WHEN OTHERS THEN
語句;
END;
顯示遊標
定義:
CURSOR <遊標名> IS <SELECT 語句> [FOR UPDATE | FOR UPDATE OF 字段];
[FOR UPDATE | FOR UPDATE OF 字段] –給遊標加鎖,既是在程序中有”UPDATE”,”INSERT”,”DELETE”語句對數據庫操作時。
遊標自動給指定的表或者字段加鎖,防止同時有別的程序對指定的表或字段進行”UPDATE”,”INSERT”,”DELETE”操作.
在使用”DELETE”,”UPDATE”後還可以在程序中使用CURRENT OF <遊標名> 子句引用當前行.
操作:
OPEN <遊標名> --打開遊標
FETCH <遊標名> INTO 變量1,變量2,變量3,....變量n,;
或者
FETCH <遊標名> INTO 行對象; --取出遊標當前位置的值
CLOSE <遊標名> --關閉遊標
屬性: %NOTFOUND --如果FETCH語句失敗,則該屬性爲"TRUE",否則爲"FALSE";
%FOUND --如果FETCH語句成果,則該屬性爲"TRUE",否則爲"FALSE";
%ROWCOUNT --返回遊標當前行的行數;
%ISOPEN --如果遊標是開的則返回"TRUE",否則爲"FALSE";
使用:
LOOP循環
示例:
DECLARE
cursor c_1 is select * from emp; --定義遊標
r c_1%rowtype; --定義一個行對象,用於獲得遊標的值
BEGIN
if c_1%isopen then
CLOSE c_1;
end if;
OPEN c_1; --判斷遊標是否打開.如果開了將其關閉,然後在打開
dbms_output.put_line('行號 姓名 薪水');
LOOP
FETCH c_1 INTO r; --取值
EXIT WHEN c_1%NOTFOUND; --如果遊標沒有取到值,退出循環.
dbms_output.put_line(c_1%rowcount||' '||r.ename||' '||r.sal); --輸出結果,需要 set serverout on 才能顯示.
END LOOP;
END;
FOR循環
示例:
DECLARE
cursor c_1 is select ename,sal from emp; --定義遊標
BEGIN
dbms_output.put_line('行號 姓名 薪水');
FOR i IN c_1 --for循環中的循環變量i爲c_1%rowtype類型;
LOOP
dbms_output.put_line(c_1%rowcount||' '||i.ename||' '||i.sal); --輸出結果,需要 set serverout on 才能顯示.
END LOOP;
END;
for循環使用遊標是在循環開始前自動打開遊標,並且自動取值到循環結束後,自動關閉遊標.
遊標加鎖示例:
DECLARE
cursor c_1 is select ename,sal from emp for update of sal; --定義遊標對emp表的sal字段加鎖.
BEGIN
dbms_output.put_line('行號 姓名 薪水');
FOR i IN c_1 --for循環中的循環變量i爲c_1%rowtype類型;
LOOP
UPDATE EMP set sal=sal+100 WHERE CURRENT OF c_1; --表示對當前行的sal進行跟新.
END LOOP;
FOR i IN c_1
LOOP
dbms_output.put_line(c_1%rowcount||' '||i.ename||' '||i.sal); --輸出結果,需要 set serverout on 才能顯示.
END LOOP;
END;
代參數的遊標
定義:
CURSOR <遊標名>(參數列表) IS
DECLARE
cursor c_1(name emp.ename%type) is select ename,sal from emp where ename=name; --定義遊標
BEGIN
dbms_output.put_line('行號 姓名 薪水');
FOR i IN c_1('&name') --for循環中的循環變量i爲c_1%rowtype類型;
LOOP
dbms_output.put_line(c_1%rowcount||' '||i.ename||' '||i.sal); --輸出結果,需要 set serverout on 才能顯示.
END LOOP;
END;
隱試遊標
隱試遊標遊標是系統自動生成的。每執行一個DML語句就會產生一個隱試遊標,起名字爲SQL;
隱試遊標不能進行”OPEN” ,”CLOSE”,”FETCH”這些操作;
屬性:
%NOTFOUND --如果DML語句沒有影響到任何一行時,則該屬性爲"TRUE",否則爲"FALSE";
%FOUND --如果DML語句影響到一行或一行以上時,則該屬性爲"TRUE",否則爲"FALSE";
%ROWCOUNT --返回遊標當最後一行的行數;
個人認爲隱試遊標的作用是判斷一個DML語句;
示例:
BEGIN
DELETE FROM EMP WHERE empno=&a;
IF SQL%NOTFOUND THEN
dbms_output.put_line('empno不存在');
END IF;
IF SQL%ROWCOUNT>0 THEN
dbms_output.put_line('刪除成功');
END IF;
END;
PL/SQL表
pl/sql表只有兩列,其中第一列爲序號列爲INTEGER類型,第二列爲用戶自定義列.
定義:
TYPE <類型名> IS TABLE OF <列的類型> [NOT NULL] INDEX BY BINARY_INTEGER;
<列的類型>可以爲Oracle的數據類行以及用戶自定義類型;
屬性方法:
.count --返回pl/sql表的總行數
.delect --刪除pl/sql表的所有內容
.delect(行數) --刪除pl/sql表的指定的行
.delct(開始行,結束行) --刪除pl/sql表的多行
.first --返回表的第一個INDEX;
.next(行數) --這個行數的下一條的INDEX;
.last --返回表的最後一個INDEX;
使用
示例:
DECLARE
TYPE mytable IS TABLE OF VARCHAR2(20) index by binary_integer; --定義一個名爲mytable的PL/sql表類型;
cursor c_1 is select ename from emp;
n number:=1;
tab_1 mytable; --爲mytable類型實例化一個tab_1對象;
BEGIN
for i in c_1
loop
tab_1(n):=i.ename; --將得到的值輸入pl/sql表
n:=n+1;
end loop;
n:=1;
tab_1.delete(&要刪除的行數); --刪除pl/sql表的指定行
for i in tab_1.first..tab_1.count
loop
dbms_output.put_line(n||' '||tab_1(n)); --打印pl/sql表的內容
n:=tab_1.next(n);
end loop;
EXCEPTION
WHEN NO_DATA_FOUND THEN --由於刪除了一行,會發生異常,下面語句可以接着刪除的行後顯示
for i in n..tab_1.count+1
loop
dbms_output.put_line(n||' '||tab_1(n));
n:=tab_1.next(n);
end loop;
END;
PL/SQL記錄
pl/sql表只有一行,但是有多列。
定義:
TYPE <類型名> IS RECORD <列名1 類型1,列名2 類型2,...列名n 類型n,> [NOT NULL]
<列的類型>可以爲Oracle的數據類行以及用戶自定義類型;可以是記錄類型的嵌套
使用
示例:
DECLARE
TYPE myrecord IS RECORD(id emp.empno%type,
name emp.ename%type,sal emp.sal%type); --定義一個名爲myrecoed的PL/sql記錄類型;
rec_1 myrecord; --爲myrecord類型實例化一個rec_1對象;
BEGIN
select empno,ename,sal into rec_1.id,rec_1.name,rec_1.sal
from emp where empno=7788; --將得到的值輸入pl/sql記錄
dbms_output.put_line(rec_1.id||' '||rec_1.name||' '||rec_1.sal); --打印pl/sql記錄的內容
END;
結合使用PL/SQL表和PL/SQL記錄
示例:
DECLARE
CURSOR c_1 is select empno,ename,job,sal from emp;
TYPE myrecord IS RECORD(empno emp.empno%type,ename emp.ename%type,
job emp.job%type,sal emp.sal%type); --定義一個名爲myrecoed的PL/sql記錄類型;
TYPE mytable IS TABLE OF myrecord index by binary_integer;
--定義一個名爲mytable的PL/sql表類型;字段類型爲PL/sql記錄類型;
n number:=1;
tab_1 mytable; --爲mytable類型實例化一個tab_1對象;
BEGIN
--賦值
for i in c_1
loop
tab_1(n).empno:=i.empno;
tab_1(n).ename:=i.ename;
tab_1(n).job:=i.job;
tab_1(n).sal:=i.sal;
n:=n+1;
end loop;
n:=1;
--輸出
for i in n..tab_1.count
loop
dbms_output.put_line(i||' '||tab_1(i).empno
||' '||tab_1(i).ename||' '||tab_1(i).job||' '||tab_1(i).sal);
end loop;
END;
強型REF遊標
定義:TYPE <遊標名> IS REF CURSOR RETURN<返回類型>;
操作:
OPEN <遊標名> For <select 語句> --打開遊標
FETCH <遊標名> INTO 變量1,變量2,變量3,....變量n,;
或者
FETCH <遊標名> INTO 行對象; --取出遊標當前位置的值
CLOSE <遊標名> --關閉遊標
屬性: %NOTFOUND --如果FETCH語句失敗,則該屬性爲"TRUE",否則爲"FALSE";
%FOUND --如果FETCH語句成果,則該屬性爲"TRUE",否則爲"FALSE";
%ROWCOUNT --返回遊標當前行的行數;
%ISOPEN --如果遊標是開的則返回"TRUE",否則爲"FALSE";
使用:
示例:
DECLARE
type c_type is ref cursor return emp%rowtype; --定義遊標
c_1 c_type; --實例化這個遊標類型
r emp%rowtype;
BEGIN
dbms_output.put_line('行號 姓名 薪水');
open c_1 for select * from emp;
loop
fetch c_1 into r;
exit when c_1%notfound;
dbms_output.put_line(c_1%rowcount||' '||r.ename||' '||r.sal); --輸出結果,需要 set serverout on 才能顯示.
END LOOP;
close c_1;
END;
弱型REF遊標
定義:TYPE <遊標名> IS REF CURSOR;
OPEN <遊標名> For <select 語句> --打開遊標
FETCH <遊標名> INTO 變量1,變量2,變量3,....變量n,;
或者
FETCH <遊標名> INTO 行對象; --取出遊標當前位置的值
CLOSE <遊標名> --關閉遊標
屬性: %NOTFOUND --如果FETCH語句失敗,則該屬性爲"TRUE",否則爲"FALSE";
%FOUND --如果FETCH語句成果,則該屬性爲"TRUE",否則爲"FALSE";
%ROWCOUNT --返回遊標當前行的行數;
%ISOPEN --如果遊標是開的則返回"TRUE",否則爲"FALSE";
示例:
set autoprint on;
var c_1 refcursor;
DECLARE
n number;
BEGIN
n:=&請輸入;
if n=1 then
open :c_1 for select * from emp;
else
open :c_1 for select * from dept;
end if;
END;
過程
定義:CREATE [OR REPLACE] PROCEDURE <過程名>[(參數列表)] IS
[局部變量聲明]
BEGIN
可執行語句
EXCEPTION
異常處理語句
END [<過程名>];
變量的類型:in 爲默認類型,表示輸入; out 表示只輸出;in out 表示即輸入又輸出;
操作以有的過程:在PL/SQL塊中直接使用過程名;在程序外使用execute <過程名>[(參數列表)]
使用:
示例:
創建過程:
create or replace procedure p_1(n in out number) is
r emp%rowtype;
BEGIN
dbms_output.put_line('姓名 薪水');
select * into r from emp where empno=n;
dbms_output.put_line(r.ename||' '||r.sal); --輸出結果,需要 set serverout on 才能顯示.
n:=r.sal;
END;
使用過程:
declare
n number;
begin
n:=&請輸入員工號;
p_1(n);
dbms_output.put_line('n的值爲 '||n);
end;
刪除過程:
DROP PROCEDURE <過程名>;
函數
定義:CREATE [OR REPLACE] FUNCTION <過程名>[(參數列表)] RETURN 數據類型 IS
[局部變量聲明]
BEGIN
可執行語句
EXCEPTION
異常處理語句
END [<過程名>];
變量的類型:in 爲默認類型,表示輸入; out 表示只輸出;in out 表示即輸入又輸出;
使用:
示例:
創建函數:
create or replace function f_1(n number) return number is
r emp%rowtype;
BEGIN
dbms_output.put_line('姓名 薪水');
select * into r from emp where empno=n;
dbms_output.put_line(r.ename||' '||r.sal); --輸出結果,需要 set serverout on 才能顯示.
return r.sal;
END;
使用函數:
declare
n number;
m number;
begin
n:=&請輸入員工號;
m:=f_1(n);
dbms_output.put_line('m的值爲 '||m);
end;
刪除函數:
DROP FUNCTION <函數名>;
數據包
定義: 定義包的規範
CREATE [OR REPLACE] PACKAGE <數據包名> AS
–公共類型和對象聲明
–子程序說明
END;
定義包的主體
CREATE [OR REPLACE] PACKAGE BODY <數據包名> AS
–公共類型和對象聲明
–子程序主體
BEGIN
-初始化語句
END;
使用:
示例:
創建數據包規範:
create or replace package pack_1 as
n number;
procedure p_1;
FUNCTION f_1 RETURN number;
end;
創建數據包主體:
create or replace package body pack_1 as
procedure p_1 is
r emp%rowtype;
begin
select * into r from emp where empno=7788;
dbms_output.put_line(r.empno||' '||r.ename||' '||r.sal);
end;
FUNCTION f_1 RETURN number is
r emp%rowtype;
begin
select * into r from emp where empno=7788;
return r.sal;
end;
end;
使用包:
declare
n number;
begin
n:=&請輸入員工號;
pack_1.n:=n;
pack_1.p_1;
n:=pack_1.f_1;
dbms_output.put_line('薪水爲 '||n);
end;
在包中使用REF遊標
示例:
創建數據包規範:
create or replace package pack_2 as
TYPE c_type is REF CURSOR; --建立一個ref遊標類型
PROCEDURE p_1(c1 in out c_type); --過程的參數爲ref遊標類型;
end;
創建數據包主體:
create or replace package body pack_2 as
PROCEDURE p_1(c1 in out c_type) is
begin
open c1 for select * from emp;
end;
end;
使用包:
var c_1 refcursor;
set autoprint on;
execute pack_2.p_1(:c_1);
刪除包:
DROP PACKAGE <包名>;
觸發器
創建觸發器:
CREATE [OR REPLACE] TRIGGER <觸發器名>
BEFORE|AFTER
INSERT|DELETE|UPDATE [OF <列名>] ON <表名>
[FOR EACH ROW]
WHEN (<條件>)
<pl/sql塊>
關鍵字"BEFORE"在操作完成前觸發;"AFTER"則是在操作完成後觸發; 關鍵字"FOR EACH ROW"指定觸發器每行觸發一次. 關鍵字"OF <列名>" 不寫表示對整個表的所有列. WHEN (<條件>)表達式的值必須爲"TRUE".
特殊變量:
:new –爲一個引用最新的列值;
:old –爲一個引用以前的列值; 這些變量只有在使用了關鍵字 “FOR EACH ROW”時才存在.且update語句兩個都有,而insert只有:new ,delect 只有:old;
使用RAISE_APPLICATION_ERROR
語法:RAISE_APPLICATION_ERROR(錯誤號(-20000到-20999),消息[,{true|false}]);
拋出用戶自定義錯誤.
如果參數爲’TRUE’,則錯誤放在先前的堆棧上.
INSTEAD OF 觸發器
INSTEAD OF 觸發器主要針對視圖(VIEW)將觸發的dml語句替換成爲觸發器中的執行語句,而不執行dml語句.禁用某個觸發器
ALTER TRIGGER <觸發器名> DISABLE
重新啓用觸發器
ALTER TRIGGER <觸發器名> ENABLE
禁用所有觸發器
ALTER TRIGGER <觸發器名> DISABLE ALL TRIGGERS
啓用所有觸發器
ALTER TRIGGER <觸發器名> ENABLE ALL TRIGGERS
刪除觸發器
DROP TRIGGER <觸發器名>
自定義對象
創建對象:
CREATE [OR REPLACE] TYPE <對象名> AS OBJECT(
屬性1 類型
屬性2 類型
方法1的規範(MEMBER PROCEDURE <過程名> 方法2的規範 (MEMBER FUNCTION <函數名> RETURN 類型)
.
PRAGMA RESTRIC_REFERENCES(<方法名>,WNDS/RNDS/WNPS/RNPS);
關鍵字”PRAGMA RESTRIC_REFERENCES”通知ORACLE函數按以下模式之一操作;
WNDS-不能寫入數據庫狀態;
RNDS-不能讀出數據庫狀態;
WNPS-不能寫入包狀態;
RNDS-不能讀出包狀態;
創建對象主體:
CREATE [OR REPLACE] TYPE body <對象名> AS 方法1的規範(MEMBER PROCEDURE <過程名> is <PL/SQL塊> 方法2的規範 (MEMBER FUNCTION <函數名> RETURN 類型 is <PL/SQL塊> END;
使用MAP方法和ORDER方法
用於對自定義類型排序。每個類型只有一個MAP或ORDER方法。 格式:MAP MEMBER FUNCTION <函數名> RETURN 類型 ORDER MEMBER FUNCTION <函數名> RETURN NUMBER
創建對象表
CREATE TABLE <表名> OF <對象類型>
示例:
- 創建name 類型
create or replace type name_type as object(
f_name varchar2(20),
l_name varchar2(20),
map member function name_map return varchar2);
create or replace type body name_type as
map member function name_map return varchar2 is --對f_name和l_name排序
begin
return f_name||l_name;
end;
end;
2 創建address 類型
create or replace type address_type as object
( city varchar2(20),
street varchar2(20),
zip number,
order member function address_order(other address_type) return number);
create or replace type body address_type as
order member function address_order(other address_type) return number is --對zip排序
begin
return self.zip-other.zip;
end;
end;
3 創建stu對象
create or replace type stu_type as object (
stu_id number(5),
stu_name name_type,
stu_addr address_type,
age number(3),
birth date,
map member function stu_map return number,
member procedure update_age);
create or replace type body stu_type as
map member function stu_map return number is --對stu_id排序
begin
return stu_id;
end;
member procedure update_age is --求年齡用現在時間-birth
begin
update student set age=to_char(sysdate,'yyyy')-to_char(birth,'yyyy') where stu_id=self.stu_id;
end;
end;
- 創建對象表
`create table student of stu_type(primary key(stu_id));`
5.向對象表插值
insert into student values(1,name_type('關','羽'),address_type('武漢','成都路',43000), null,sysdate-365*20);
6.使用對象的方法
delcare
aa stu_type;
begin
select value(s) into aa from student s where stu_id=1; --value()將對象表的每一行轉成行對象括號中必須爲表的別名
aa.update_age();
end;
7.select stu_id,s.stu_name.f_name,s.stu_name.l_name from student s; –查看類型的值
8.select ref(s) from student s ; –ref()求出行對象的OID,括號中必須爲表的別名;deref()將oid變成行隊像;
其他
1.在PL/SQL中使用DDL
將sql語句賦給一個varchar2變量,在用execute immediate 這個varchar2變量即可;
示例:
declare
str varchar2(200);
begin
str:='create table test(id number,name varchar2(20))'; --創建表
execute immediate str;
str:='insert into test values(3,''c'')'; --向表裏插數據
execute immediate str;
end;
但是要隊這個表插入數據也必須使用execute immediate 字符變量
2.判斷表是否存在;
示例:
declare
n tab.tname%type;
begin
select tname into n from tab where tname='&請輸入表名';
dbms_output.put_line('此表以存在');
exception
when no_data_found then
dbms_output.put_line('還沒有此表');
end;
2.查看以有的過程;
示例:
select object_name,object_type,status from user_objects where object_type='PROCEDURE';