PLSQL一些常用的知識點

huan1993發表於2023-05-13

1、背景

此處簡單的記錄一下在 oracle中如何使用plsql語法,記錄一些簡單的例子,防止以後忘記。

2、變數的宣告

declare
    -- 宣告變數
    v_name varchar2(20);
    -- 此變數由 select into 賦值
    v_man_sex number;
    -- v_sex 變數的型別和 student表中的 sex 欄位的型別一致
    v_sex student.sex%TYPE;
    -- v_row 中儲存的是 student表中的一整行欄位, 也可以是遊標中的一整行
    v_row student%rowtype;
    -- 宣告變數並賦值
    v_addr varchar2(100) := '湖北省';
    -- 宣告日期變數
    v_date date := sysdate;
    
    -- 定義一個記錄型別
    type STUDENT_INFO is record
     (
        student_id student.student_id%TYPE,
        student_name student.student_name%TYPE
     );
    -- 定義基於記錄的巢狀表
    type nested_student_info is table of STUDENT_INFO;
    -- 宣告變數
    student_list nested_student_info;

begin
    -- 直接賦值
    v_name := '直接賦值';
    v_date := to_date('2023-12-12', 'yyyy-mm-dd');
    -- 單個欄位語句賦值
    select count(*) into v_man_sex from student where sex = 1;
    -- 多個欄位賦值
    select student_name,sex into v_name,v_sex from student where student_id = 'S003';
    -- 獲取一行資料 ( 此處需要查詢出所有的欄位,否則可能報錯 )
    select student_id,student_name,sex,CREATE_TIME into v_row from student where student_id = 'S002';
    -- 列印輸出
    DBMS_OUTPUT.PUT_LINE('日期:' || v_date || '姓名:' || v_name || ',' || v_row.STUDENT_NAME || ' 男生人數:' || v_man_sex || ' 地址:' || v_addr );
end;

3、if 判斷

統計總共有多少個學生,並進行if判斷。

declare
    -- 宣告一個變數,記錄有多少個學生
    v_student_count number;
begin
    -- 給 v_student_count 變數賦值
    select count(*) into v_student_count from student;

    -- 執行if判斷

    if v_student_count > 3 then
        DBMS_OUTPUT.PUT_LINE('當前學生數為: [' || v_student_count || ']>3');
    elsif v_student_count >=2 then
        DBMS_OUTPUT.PUT_LINE('當前學生數為: [' || v_student_count || '] in [2,3]');
    else
        DBMS_OUTPUT.PUT_LINE('當前學生數為: [' || v_student_count || ']<2');
    end if;
end;

4、case

-- case
declare
    -- 宣告一個變數,記錄有多少個學生
    v_student_count number;
begin
    -- 給 v_student_count 變數賦值
    select count(*) into v_student_count from student;

    -- 執行if判斷

    case when v_student_count > 3 then
        DBMS_OUTPUT.PUT_LINE('當前學生數為: [' || v_student_count || ']>3');
    when v_student_count >=2 then
        DBMS_OUTPUT.PUT_LINE('當前學生數為: [' || v_student_count || '] in [2,3]');
    else
        DBMS_OUTPUT.PUT_LINE('當前學生數為: [' || v_student_count || ']<2');
    end case;
end;

5、迴圈

輸出1到100

1、loop 迴圈

declare
    -- 定義一個變數並賦值
    v_count number := 1;
begin
    loop
        -- 提出條件
        exit when v_count > 100;
        DBMS_OUTPUT.PUT_LINE('當前 count = ' || v_count);
        -- v_count 加1
        v_count := v_count + 1;
    end loop;
end;

2、while 迴圈

-- while 迴圈
declare
    -- 定義一個變數並賦值
    v_count number := 1;
begin
    while v_count <= 100 loop
        DBMS_OUTPUT.PUT_LINE('當前 count = ' || v_count);
        -- v_count 加1
        v_count := v_count + 1;
    end loop;
end;

3、for迴圈

-- for 迴圈
declare
    -- 定義一個變數
    v_count number;
begin
    for v_count in 1..100 loop
        DBMS_OUTPUT.PUT_LINE('當前 count = ' || v_count);
    end loop;
end;

6、遊標

1、無引數的遊標

-- 遊標
declare
    -- 宣告一個遊標
    cursor cur_student is select student_id,student_name,sex from student;
    -- 宣告變數
    row_cur_student cur_student%rowtype;
begin
    -- 開啟遊標
    open cur_student;

    -- 遍歷資料
    loop
        -- 獲取一行資料
        fetch cur_student into row_cur_student;
        -- 退出
        exit when cur_student%NOTFOUND;
        -- 執行業務邏輯(此句如果移動到exit when上方,則可能會多列印一句)
        DBMS_OUTPUT.PUT_LINE('studentId:' || row_cur_student.STUDENT_ID || ' studentName:' || row_cur_student.STUDENT_NAME);

    end loop;

    -- 關閉遊標
    close cur_student;
end;

2、帶引數的遊標

declare
    -- 宣告一個遊標, 需要傳遞v_student_id引數
    cursor cur_student(v_student_id student.student_id%TYPE) is
        select student_id,student_name,sex from student where student_id = v_student_id;
    -- 宣告變數
    row_cur_student cur_student%rowtype;
    -- 此變數透過查詢獲取值,然後帶到遊標中
    v_query_student_id student.student_id%TYPE;
begin
    -- 開啟遊標
    --引數傳遞方式一: open cur_student('S001');

    -- 引數傳遞方式二:
    select 'S001' into v_query_student_id from dual;
    open cur_student(v_query_student_id);

    -- 遍歷資料
    loop
        -- 獲取一行資料
        fetch cur_student into row_cur_student;
        -- 退出
        exit when cur_student%NOTFOUND;
        -- 執行業務邏輯(此句如果移動到exit when上方,則可能會多列印一句)
        DBMS_OUTPUT.PUT_LINE('studentId:' || row_cur_student.STUDENT_ID || ' studentName:' || row_cur_student.STUDENT_NAME);

    end loop;

    -- 關閉遊標
    close cur_student;
end;

7、執行ddl dml

需要放到 execute immediate中執行,否則會報錯。

declare
    v_table_name varchar2(20) := 'student_bak';
    -- 拼接一個動態SQL
    v_sql varchar2(100);
begin
    execute immediate 'create table student_bak as select * from student';
    execute immediate 'alter table student_bak add new_cloumn varchar2(20)';

    -- 帶變數的執行
    v_sql := 'drop table ' || v_table_name;
    execute immediate v_sql;

end;

8、儲存過程

1、無引數的儲存過程

-- 無引數的儲存過程
create or replace procedure sp_print_all_student
is
    -- 宣告一個遊標
    cursor c_all_student is select student_id,student_name from student;
    -- 宣告一個變數
    row_student c_all_student%rowtype;
begin
    -- 迴圈遊標
    for row_student in c_all_student loop
        DBMS_OUTPUT.PUT_LINE(row_student.STUDENT_ID || ' ' || row_student.STUDENT_NAME);
    end loop;
end;
-- 呼叫
begin
    SP_PRINT_ALL_STUDENT();
end;

2、有輸入輸出引數的儲存過程

-- 有引數的儲存過程
create or replace procedure sp_find_student(/** 輸入引數 */ i_student_id in student.student_id%TYPE,
                                           /** 輸出引數 */ o_student_name out student.student_name%TYPE)
IS
    -- 定義變數並賦值
    v_student_id varchar2(64) := i_student_id;
begin
    DBMS_OUTPUT.PUT_LINE('v_student_id:' || v_student_id);
    -- 將查詢到的 student_name 賦值到 o_student_name
    select student_name into o_student_name from student where student_id = i_student_id;
end;

declare
    -- 定義一個變數用於接收儲存過程的返回值
    output_student_name student.student_name%TYPE;
begin
    sp_find_student('S001', output_student_name);
    -- 輸出儲存過程的返回值
    DBMS_OUTPUT.PUT_LINE(output_student_name);
end;

3、merge into 的使用

存在更新,不存在插入。

create or replace procedure sp_merge_into(i_student_id in varchar2)
IS
begin
    -- 如果 using 中查詢出來的資料,透過 on 條件匹配的話,則更新 student_bak表,否則插入student_bak表
    merge into STUDENT_BAK t
    using (select * from student where student_id = i_student_id) s
    on ( t.student_id = s.student_id )
    when matched then update set
                                 -- t.STUDENT_ID = s.STUDENT_ID, on中的條件不可更新
                                 t.STUDENT_NAME = s.STUDENT_NAME,
                                 t.SEX = s.SEX,
                                 t.CREATE_TIME = s.CREATE_TIME
    when not matched then insert(student_id, student_name, create_time) values (
                                         s.STUDENT_ID,
                                         s.STUDENT_NAME,
                                         s.CREATE_TIME
                                        );
    commit ;
end;

4、測試異常

create or replace procedure sp_error
IS
    v_num number;
begin
    DBMS_OUTPUT.PUT_LINE('測試異常');

    -- 產生異常
    v_num := 1 / 0;

    exception -- 儲存過程異常
        when too_many_rows then
                dbms_output.put_line('返回值多於1行');
        when others then
              -- 異常處理方法,可以是列印錯誤,然後進行回滾等操作,下面操作一樣,看自己情況決定
              rollback;
              dbms_output.put_line('錯誤碼:' ||sqlcode);
              dbms_output.put_line('異常資訊:' || substr(sqlerrm, 1, 512));
end;

begin
    sp_error();
end;

5、bulk into & record

1、select into 中使用 bulk into & record

create or replace procedure sp_bulk_collect_01
IS
    -- 定義一個記錄型別
    type STUDENT_INFO is record
     (
        student_id student.student_id%TYPE,
        student_name student.student_name%TYPE
     );

    -- 定義基於記錄的巢狀表
    type nested_student_info is table of STUDENT_INFO;
    -- 宣告變數
    student_list nested_student_info;
begin
    -- 使用 bulk collect into 將所獲取的結果集一次性繫結到記錄變數 student_list 中
    select student_id,student_name bulk collect into student_list from student;

    -- 遍歷
    for i in student_list.first .. student_list.last loop
        DBMS_OUTPUT.PUT_LINE('studentId:' || student_list(i).student_id || ' studentName:' || student_list(i).student_name);
    end loop;
end;

begin
    sp_bulk_collect_01;
end;

2、fetch into 中使用 bulk into & forall


-- bulk collect
create or replace procedure sp_bulk_collect_02
IS
    -- 定義一個遊標
    cursor cur_student is select student_id,student_name,sex,create_time from student;
    -- 定義基於遊標的巢狀表
    type nested_student_info is table of cur_student%rowtype;
    -- 宣告變數
    student_list nested_student_info;
begin
    -- 開啟遊標
    open cur_student;
        loop
            -- 一次獲取2條資料插入到 student_list 中
            fetch cur_student bulk collect into student_list limit 2;
            -- 退出
            --exit when student_list%notfound; 不可使用這種方式
            exit when student_list.count = 0;

            -- 輸出
            for i in student_list.first .. student_list.last loop
                DBMS_OUTPUT.PUT_LINE('studentId:' || student_list(i).student_id || ' studentName:' || student_list(i).student_name);
            end loop;

            -- 使用 forall 更新資料, 可以將多個dml語句批次傳送給SQL引擎,提高執行效率。
            forall i in student_list.first .. student_list.last
                update student set student_name = student_list(i).STUDENT_NAME || '_update' where student_id = student_list(i).STUDENT_ID;
            commit ;
        end loop;

    -- 關閉遊標
    close cur_student;
end;

begin
    sp_bulk_collect_02;
end;

6、接收陣列引數

-- 建立StudentIdList陣列的長度是4,每一項最多存20個字元
create or replace type StudentIdList as varray(4) of varchar2(20);

-- 建立儲存過程,接收陣列引數
create or replace procedure sp_param_list(studentIdList in StudentIdList)
is
begin
    for i in 1..studentIdList.COUNT loop
        DBMS_OUTPUT.PUT_LINE('studentId:' || studentIdList(i));
    end loop;
end;
declare
 begin
    sp_param_list(STUDENTIDLIST('d','c','S001','S0021222222222233'));
end;

7、接收陣列物件,並將陣列物件轉換成表使用

-- 建立資料庫物件
create or replace type StudentInfo is object(
    studentId varchar2(64),
    studentName varchar2(64)
);
-- 建立陣列物件
create or replace type StudentInfoArr as table of StudentInfo;

-- 建立儲存過程
create or replace procedure sp_param_list_02(arr in StudentInfoArr)
is
    -- 宣告一個變數,記錄傳遞進來的arr的數量
    v_student_count number := 0;
begin
    -- 傳遞進來的陣列轉換成使用
    select count(*) into v_student_count from table(cast(arr AS StudentInfoArr))
    where studentId like 'S%';
    DBMS_OUTPUT.PUT_LINE('傳遞進來學生學號以S開頭的學生有: ' || v_student_count || '個');

    -- 輸出列表引數
    for i in 1..arr.COUNT loop
        DBMS_OUTPUT.PUT_LINE('studentId:' || arr(i).studentId || ' studentName:' || arr(i).studentName);
    end loop;
end;

declare
begin
    sp_param_list_02(arr => StudentInfoArr(StudentInfo('S001','張三'),StudentInfo('S002','李四')));
end;

8、返回多個引數

create or replace procedure sp_return_value(stuInfoList out Sys_Refcursor)
IS
begin
    open stuInfoList for select STUDENT_ID,STUDENT_NAME,SEX from STUDENT;
end;

declare
    stu Sys_Refcursor;
    v_student_id STUDENT.STUDENT_ID%TYPE;
    v_student_name STUDENT.STUDENT_NAME%TYPE;
    v_sex STUDENT.SEX%TYPE;
begin
    SP_RETURN_VALUE(  stu);
    loop
        fetch stu into v_student_id,v_student_name,v_sex;
        exit when stu%notfound;
        DBMS_OUTPUT.PUT_LINE('studentId:' || v_student_id || ' studentName: ' || v_student_name);
    end loop;
 end;

9、程式包 package

1、定義包頭

包頭可以簡單的理解java中的介面。

create or replace package pkg_huan as
    v_pkg_name varchar2(30) := 'pkg_huan';
    function add(param1 in number, param2 in number) return number;
    procedure sp_pkg_01;
    procedure sp_pkg_02(param1 in varchar2);
end pkg_huan;

2、實現包體

包體可以簡單的理解java中的實現介面的類。

create or replace package body  pkg_huan as
    -- 實現function
    function add(param1 in number, param2 in number) return number IS
    begin
        return param1 + param2;
    end;
    -- 實現無引數的儲存過程
    procedure sp_pkg_01 as
    begin
        DBMS_OUTPUT.PUT_LINE('package name:' || v_pkg_name || 'procedure name: sp_pkg_01');
    end;
    -- 實現有引數的儲存過程
    procedure sp_pkg_02(param1 in varchar2) as
    begin
        DBMS_OUTPUT.PUT_LINE('param1:' || param1);
    end;
end;

3、呼叫包中的方法或儲存過程

begin
    -- 呼叫方法
    DBMS_OUTPUT.PUT_LINE('1+2=' || PKG_HUAN.add(1,2));
    -- 呼叫無引數的儲存過程
    PKG_HUAN.sp_pkg_01();
    -- 呼叫有引數的儲存過程
    PKG_HUAN.sp_pkg_02(12);
end;

10、參考連結

1、http://www.cis.famu.edu/support/10g/Oracle_Database_10g/doc/appdev.102/b14261/objects.htm

相關文章