oracle數據庫(六)_PLSQL
格式
[DECLARE]
--declaration statements ①
BEGIN
--executable statements ②
[EXCEPTION] --exception statements ③
END;
類型
引用類型
%type
引用特定類型
%rowtype
應用行對象類型
複合類型
- 自定義行對象
-- 自定義行對象
declare
type tp_row is record(
empno number(24),
ename varchar2(24)
);
my_row tp_row;
begin
-- 使用行對象
select empno,ename into my_row from emp where rownum = 1;
dbms_output.put_line(my_row.empno
||','
||my_row.ename);
end;
- 列類型(一維表序列)
-- 自定義列類型(一維表序列)
declare
type tp_col is table of varchar2(36); -- index by binary_integer;
my_col tp_col;
begin
-- 迭代列類型
select ename bulk collect into my_col from emp;
-- for idx in my_col.first..my_col.last
for idx in 1..my_col.count loop
dbms_output.put_line(my_col(idx));
end loop;
end;
- 表類型(多維表類型)
-- 自定義表類型(多維表類型)
declare
type tp_row is record(
empno number(24),
ename varchar2(24)
);
type tp_frame is table of tp_row; -- index by binary_integer
my_row tp_row;
my_frame tp_frame;
begin
-- 使用表類型
select empno,ename bulk collect into my_frame from emp;
for idx in my_frame.first..my_frame.last loop
dbms_output.put_line(my_frame(idx).empno||','||my_frame(idx).ename);
end loop;
end;
數組
DECLARE
type namesarray IS VARRAY(6) OF VARCHAR2(10);
type grades IS VARRAY(6) OF number(24);
names namesarray;
marks grades;
total integer;
BEGIN
names := namesarray('Kavita', 'Pritam', 'Ayan', 'Rishav', 'Aziz');
marks:= grades(98, 97, 78, 87, 92);
total := names.count;
dbms_output.put_line('Total '|| total || ' Students');
FOR i in 1 .. total LOOP
dbms_output.put_line('Student: ' || names(i) || '
Marks: ' || marks(i));
END LOOP;
END;
序列屬性
arr.count
數組數量
arr.first
第一個下下標值
arr.last
最後的下標值
arr.delete
清空數組
arr.delete(num)
刪除下標數據,table類型纔可以用
arr.extend
下標長度加一
arr(num):=val
賦值,table可以直接賦值,arr需要先extend再賦值
hello world
-- hello world
declare
ss varchar2(24);
df number default 3; -- 默認值
PI CONSTANT NUMBER := 3.14; -- 常量
begin
ss := 'hello world';
dbms_output.put_line(ss);
end;
--
declare
emprow emp%rowtype;
begin
select * into emprow from emp where empno = 7934;
dbms_output.put_line(emprow.ename);
end;
--
begin
dbms_output.put_line('hello world');
end;
流程控制
if
-- if
declare
sal emp.sal%type;
begin
select sal into sal from emp where ename = 'JAMES';
if sal > 1500 then
dbms_output.put_line(1000);
elsif sal > 900 then
dbms_output.put_line(800);
else
dbms_output.put_line(400);
end if;
end;
case
-- case
declare
sal emp.sal%type;
begin
select sal into sal from emp where ename = 'JAMES';
case
when sal > 1500 then
dbms_output.put_line(1000);
when sal > 900 then
dbms_output.put_line(800);
else
dbms_output.put_line(400);
end case;
end;
循環
loop
-- loop循環
-- 1-100累計和
declare
counter number(6):= 0;
sumresult number(6):= 0;
begin
loop
counter := counter+1;
sumresult := sumresult+counter;
exit when counter >= 100;
end loop;
dbms_output.put_line('result:'||to_char(sumresult));
end;
while
-- while循環
-- 1-100累計和
declare
counter number(6):= 0;
sumresult number(6):= 0;
begin
while counter<100 loop
counter := counter+1;
sumresult := sumresult+counter;
end loop;
dbms_output.put_line('result:'||to_char(sumresult));
end;
for
-- FOR
BEGIN
FOR cnt IN 1..100 LOOP
DBMS_OUTPUT.PUT_LINE(cnt);
END LOOP;
END;
遊標
定義
- 遊標可以將查詢結果集中的記錄逐一取出
- 隱形遊標,在查詢返回一行時自動創建
- 顯式遊標,當查詢返回多行是必須自行創建
使用
-- OPEN curs 打開遊標,遊標知道第一行
-- FETCH curs INTO val 獲取遊標值,並把遊標指向下一行
-- CLOSE curs 關閉遊標
-- %ISOPEN 是否打開
-- %FOUND | %NOTFOUND 是否找到數據返回布爾值
-- %ROWCOUNT 一取數據行數
-- 標準迭代行數據
DECLARE
CURSOR c_code IS
SELECT "ts_code","trade_date","close" FROM "code" WHERE "trade_date" > TO_DATE('20191001','yyyymmdd');
t_row c_code%ROWTYPE;
ret VARCHAR2(255);
BEGIN
OPEN c_code;
LOOP
FETCH c_code INTO t_row;
EXIT WHEN c_code%NOTFOUND;
ret := t_row."ts_code"||' in '||TO_CHAR(t_row."trade_date",'yyyy/mm/dd')||' close is '||t_row."close";
DBMS_OUTPUT.PUT_LINE(ret);
END LOOP;
CLOSE c_code;
END;
-- for迭代行數據
DECLARE
CURSOR c_code IS
SELECT "ts_code","trade_date","close" FROM "code" WHERE "trade_date" > TO_DATE('20191001','yyyymmdd');
t_row c_code%ROWTYPE;
ret VARCHAR2(255);
BEGIN
FOR row in c_code LOOP
ret := row."ts_code"||' in '||TO_CHAR(row."trade_date",'yyyy/mm/dd')||' close is '||row."close";
DBMS_OUTPUT.PUT_LINE(ret);
END LOOP;
END;
引用遊標類型
declare
type tp_curs is ref cursor;
curs tp_curs;
r_emp emp%rowtype;
begin
open curs for select * from emp;
loop
fetch curs into r_emp;
exit when curs%notfound;
dbms_output.put_line(r_emp.ename||'--'||r_emp.sal);
end loop;
end;
動態SQL
-- 動態SQL
EXECUTE IMMEDIATE 動態語句字符串
[INTO 變量列表]: 把查詢的結果保存到INTO後面的變量中
[USING 參數列表]: USING爲語句中的參數傳值,格式是[:參數名]
BEGIN
EXECUTE IMMEDIATE 'insert into table values(:date1,:date2)'
[into v_ret] USING in 100,in 200;
END;
存儲與函數
區別與特性
- 函數必須有返回值,包含return語句
- 函數可以在查詢語句中直接調用,過程需要單獨調用
- 函數一般用於計算,過程一般用於數據操作
- 返回1個值時用函數,返回多個值用過程
參數
- in 輸入參數,不可賦值
- out輸出參數,可以賦值
- in out,輸入或輸出,可以取值及賦值
使用過程
- 命名標準,函數名sp_name,參數名p_attr,局部變量v_val
- return 強制退出不執行後面代碼,返回null
set serveroutput on;
-- 創建過程
create or replace procedure sp_test(p_val in varchar2)
as
v_val varchar2(24) := 'hello sp_test';
begin
dbms_output.put_line(p_val);
dbms_output.put_line(v_val);
end sp_test;
-- 調用存儲過程
begin
sp_test('abc');
end;
-- 刪除過程
drop procedure sp_test;
使用函數
- 命名標準,函數名fc_name,參數名p_attr,局部變量v_val
- return null 強制退出後面代碼不執行
set serveroutput on;
-- 創建函數
set serveroutput on;
create or replace function fc_test(p_val in varchar2)
return number
as
v_val varchar2(24) := 'hello sp_test';
begin
dbms_output.put_line(p_val);
dbms_output.put_line(v_val);
return 1;
end fc_test;
-- 調用
begin
dbms_output.put_line(fc_test('123'));
end;
-- 刪除
drop function fc_test;
包
結構
-- 包頭(包內全局變量)
create [or replace] package pg_name as
類型;
遊標;
變量;
函數;
過程;
end [pg_name];
-- 包體
create [or replace] package body pg_name as
函數;
過程;
end [pg_name];
包頭
create or replace package pk_mypg as
-- 變量
n number(4);
-- 遊標
cursor c_emp is select * from emp;
-- 類型
type tp_cursor is ref cursor;
function getempcurs return tp_cursor;
-- 過程
procedure mypg_sp_02(p_empno in emp.empno%type);
-- 函數
function mypg_fc_08(p_empno in emp.empno%type) return emp.sal%type;
end;
包體
create or replace package body pk_mypg as
-- 返回遊標
function getempcurs return tp_cursor as
curs tp_cursor;
begin
open curs for
select * from emp;
return curs;
end getempcurs;
-- 執行sp_02函數
procedure mypg_sp_02(p_empno in emp.empno%type) as
begin
sp_02(p_empno);
end mypg_sp_02;
-- 執行fc_08函數
function mypg_fc_08(p_empno in emp.empno%type) return emp.sal%type as
begin
return fc_08(p_empno);
end mypg_fc_08;
end;
調用
-- 調用
declare
c_emp pk_mypg.tp_cursor;
r_emp emp%rowtype;
begin
-- 返回遊標
c_emp:=pk_mypg.getempcurs;
-- 迭代遊標
loop
fetch c_emp into r_emp;
exit when c_emp%notfound;
-- 打印
dbms_output.put_line(r_emp.empno||'--'||r_emp.ename);
end loop;
end;
- 其他
desc pk_mypg
查看包結構
觸發器
結構
create or replace trigger name
before or after
delete or insert or update of [col]
on tbl
[for each row]
[when condition]
[declare]
begin
end;
行級觸發器
- 寫日誌記錄
-- 寫日誌記錄
create or replace trigger modify_stu
after insert or delete or update of stu_name
on student_123
for each row
begin
if inserting then
insert into stu_log_123 values(1,'insert',sysdate,:new.stu_name);
elsif deleting then
insert into stu_log_123 values(2,'delete',sysdate,:old.stu_name);
elsif updating then
insert into stu_log_123 values(3,'update_old',sysdate,:old.stu_name);
insert into stu_log_123 values(4,'update_new',sysdate,:new.stu_name);
end if;
end;
語句級觸發器
- 控制對錶的修改
-- 控制對錶的修改
create or replace trigger banl_modify_stu
before insert or delete or update
on student_123
begin
if inserting then
raise_application_error(-20001,'該表不允許插入');
elsif deleting then
raise_application_error(-20002,'該表不允許刪除');
elsif updating then
raise_application_error(-20003,'該表不允許修改');
end if;
end;
同步
- merge
merge into emp1 e1
using emp e
on (e.empno = e1.empno)
when matched then
update
set e1.ename = e.ename,
e1.job = e.job,
e1.mgr = e.mgr,
e1.hiredate = e.hiredate,
e1.sal = e.sal,
e1.comm = e.comm,
e1.deptno = e.deptno
when not matched then
insert
(e1.empno,
e1.ename,
e1.job,
e1.mgr,
e1.hiredate,
e1.sal,
e1.comm,
e1.deptno)
values
(e.empno, e.ename, e.job, e.mgr, e.hiredate, e.sal, e.comm, e.deptno);
日誌
例外
- 異常直接跳到異常捕捉,中間代碼不執行
內部例外
-- 內部例外
declare
p number;
begin
p := 1/0;
exception
when DUP_VAL_ON_INDEX then
dbms_output.put_line('試圖向唯一索引列插入重複值');
when INVALID_CURSOR then
dbms_output.put_line('試圖進行非法遊標操作');
when INVALID_NUMBER then
dbms_output.put_line('試圖將字符串轉換爲數字');
when NO_DATA_FOUND then
dbms_output.put_line('SELECT INTO語句中沒有返回任何記錄');
when TOO_MANY_ROWS then
dbms_output.put_line('SELECT INTO語句中返回多於1條記錄');
when ZERO_DIVIDE then
dbms_output.put_line('試圖用0作爲除數');
when CURSOR_ALREADY_OPEN then
dbms_output.put_line('試圖打開一個已經打開的遊標');
when others then
dbms_output.put_line('其他');
end;
自定義例外
-- 自定義例外
declare
p number;
myex exception;
begin
p:=0;
if p = 0 then
raise myex;
end if;
exception
when myex
then dbms_output.put_line('自定義例外');
when others
then dbms_output.put_line('其他例外');
end;
拋出系統異常
begin
raise_application_error(-20001,'系統例外'); -- [20001-20999]
end;
其他
-- 解除內存限制
DBMS_OUTPUT.ENABLE(buffer_size => null);
-- 回車
chr(10);