Oracle速查語法:PL/SQL
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';