1. 程式人生 > >Oracle速查語法:PL/SQL

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; 

a

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 <物件型別>

示例:

  1. 建立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; 
  1. 建立物件表
    `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';

結束