最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Oracle數(shù)據(jù)庫PL/SQL 存儲過程與函數(shù)完全指南

 更新時間:2025年12月30日 09:48:54   作者:廋到被風(fēng)吹走  
這篇文章詳細介紹了Oracle PL/SQL中的存儲過程和函數(shù),包括它們的核心概念、語法結(jié)構(gòu)、參數(shù)模式、創(chuàng)建與調(diào)用方法、高級特性、包的使用、性能優(yōu)化技巧、調(diào)試與監(jiān)控、最佳實踐與規(guī)范,以及存儲過程和函數(shù)的終極對比,感興趣的朋友跟隨小編一起看看吧

Oracle PL/SQL 存儲過程與函數(shù)完全指南

存儲過程(Procedure)和函數(shù)(Function)是 PL/SQL 的核心可執(zhí)行單元,用于封裝業(yè)務(wù)邏輯、提升性能、增強安全性和代碼復(fù)用。

一、核心概念與區(qū)別

1.1 存儲過程 vs 函數(shù)

特性存儲過程 (Procedure)函數(shù) (Function)
返回值通過 OUT 參數(shù)返回,可返回多個值必須返回單個值(通過 RETURN
調(diào)用方式EXECUTE/CALL 或 PL/SQL 塊中調(diào)用可在 SQL 語句中直接調(diào)用
用途執(zhí)行操作(插入、更新、批量處理)計算并返回值(如公式、轉(zhuǎn)換)
事務(wù)控制可包含 COMMIT/ROLLBACK通常不包含事務(wù)控制
性能適合復(fù)雜業(yè)務(wù)邏輯適合計算密集型操作

1.2 基本語法結(jié)構(gòu)

-- 存儲過程語法
CREATE [OR REPLACE] PROCEDURE 過程名 (
    參數(shù)1 [模式] 數(shù)據(jù)類型,
    參數(shù)2 [模式] 數(shù)據(jù)類型
) [AUTHID {DEFINER | CURRENT_USER}] -- 權(quán)限模型
IS|AS
    -- 聲明部分(變量、游標(biāo)、類型)
    變量聲明;
BEGIN
    -- 執(zhí)行部分
    可執(zhí)行語句;
EXCEPTION
    -- 異常處理部分
    異常處理;
END [過程名];
/
-- 函數(shù)語法
CREATE [OR REPLACE] FUNCTION 函數(shù)名 (
    參數(shù)1 [模式] 數(shù)據(jù)類型
) RETURN 返回數(shù)據(jù)類型
IS|AS
    變量聲明;
BEGIN
    RETURN 返回值;
EXCEPTION
    異常處理;
END [函數(shù)名];
/

二、創(chuàng)建與調(diào)用

2.1 創(chuàng)建存儲過程

-- 場景:調(diào)整員工薪水并記錄日志
CREATE OR REPLACE PROCEDURE adjust_employee_salary (
    p_emp_id IN NUMBER,           -- 員工ID(輸入?yún)?shù))
    p_percent IN NUMBER,          -- 調(diào)整百分比(輸入?yún)?shù))
    p_new_salary OUT NUMBER,      -- 新薪水(輸出參數(shù))
    p_message OUT VARCHAR2        -- 消息(輸出參數(shù))
) AUTHID DEFINER
IS
    -- 聲明變量
    v_old_salary employees.salary%TYPE;
    v_emp_name VARCHAR2(100);
BEGIN
    -- 1. 查詢當(dāng)前薪水
    SELECT salary, first_name || ' ' || last_name
    INTO v_old_salary, v_emp_name
    FROM employees
    WHERE employee_id = p_emp_id;
    -- 2. 計算新薪水
    p_new_salary := v_old_salary * (1 + p_percent / 100);
    -- 3. 更新薪水
    UPDATE employees
    SET salary = p_new_salary
    WHERE employee_id = p_emp_id;
    -- 4. 記錄日志
    INSERT INTO salary_log (emp_id, old_salary, new_salary, change_date)
    VALUES (p_emp_id, v_old_salary, p_new_salary, SYSDATE);
    -- 5. 設(shè)置返回消息
    p_message := '員工 ' || v_emp_name || ' 薪水已從 ' || v_old_salary || ' 調(diào)整為 ' || p_new_salary;
    -- 6. 提交事務(wù)
    COMMIT;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        p_message := '錯誤:員工ID ' || p_emp_id || ' 不存在';
        ROLLBACK;
    WHEN OTHERS THEN
        p_message := '錯誤:' || SQLERRM;
        ROLLBACK;
END adjust_employee_salary;
/

2.2 調(diào)用存儲過程

-- 方式1:匿名塊調(diào)用
DECLARE
    v_new_salary NUMBER;
    v_message VARCHAR2(200);
BEGIN
    adjust_employee_salary(p_emp_id => 101, 
                           p_percent => 10, 
                           p_new_salary => v_new_salary, 
                           p_message => v_message);
    DBMS_OUTPUT.PUT_LINE(v_message);
    DBMS_OUTPUT.PUT_LINE('新薪水:' || v_new_salary);
END;
/
-- 方式2:EXECUTE 命令(SQL*Plus)
VARIABLE new_salary NUMBER
VARIABLE message VARCHAR2(200)
EXEC adjust_employee_salary(101, 10, :new_salary, :message);
PRINT new_salary
PRINT message
-- 方式3:JDBC 調(diào)用(Java)
CallableStatement cstmt = conn.prepareCall("{call adjust_employee_salary(?, ?, ?, ?)}");
cstmt.setInt(1, 101);
cstmt.setDouble(2, 10);
cstmt.registerOutParameter(3, Types.NUMERIC);
cstmt.registerOutParameter(4, Types.VARCHAR);
cstmt.execute();
double newSalary = cstmt.getDouble(3);
String message = cstmt.getString(4);

三、參數(shù)模式詳解

3.1 IN 模式(默認(rèn))

-- 輸入?yún)?shù),只讀
CREATE PROCEDURE process_order (
    p_order_id IN NUMBER,      -- 輸入訂單ID
    p_status OUT VARCHAR2
) IS
BEGIN
    -- p_order_id 可被讀取,但不能被修改
    UPDATE orders SET status = 'PROCESSING' WHERE order_id = p_order_id;
    p_status := '處理完成';
END;
/

3.2 OUT 模式

-- 輸出參數(shù),用于返回值
CREATE PROCEDURE get_employee_info (
    p_emp_id IN NUMBER,
    p_name OUT VARCHAR2,
    p_salary OUT NUMBER,
    p_hire_date OUT DATE
) IS
BEGIN
    SELECT first_name || ' ' || last_name, salary, hire_date
    INTO p_name, p_salary, p_hire_date
    FROM employees
    WHERE employee_id = p_emp_id;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        p_name := NULL;
        p_salary := NULL;
        p_hire_date := NULL;
END;
/

3.3 IN OUT 模式

-- 輸入輸出參數(shù),既可讀也可寫
CREATE PROCEDURE swap_values (
    p_value1 IN OUT NUMBER,
    p_value2 IN OUT NUMBER
) IS
    v_temp NUMBER;
BEGIN
    v_temp := p_value1;
    p_value1 := p_value2;
    p_value2 := v_temp;
END;
/
-- 調(diào)用
DECLARE
    a NUMBER := 10;
    b NUMBER := 20;
BEGIN
    DBMS_OUTPUT.PUT_LINE('交換前:a=' || a || ', b=' || b);
    swap_values(a, b);
    DBMS_OUTPUT.PUT_LINE('交換后:a=' || a || ', b=' || b);
END;
/

3.4 參數(shù)默認(rèn)值

CREATE OR REPLACE PROCEDURE create_employee (
    p_first_name IN VARCHAR2,
    p_last_name IN VARCHAR2,
    p_salary IN NUMBER DEFAULT 5000,  -- 默認(rèn)值
    p_department_id IN NUMBER DEFAULT 50
) IS
BEGIN
    INSERT INTO employees (employee_id, first_name, last_name, salary, department_id)
    VALUES (emp_seq.NEXTVAL, p_first_name, p_last_name, p_salary, p_department_id);
    COMMIT;
END;
/
-- 調(diào)用(使用默認(rèn)值)
EXEC create_employee('John', 'Doe');
-- 調(diào)用(覆蓋默認(rèn)值)
EXEC create_employee('Jane', 'Smith', 8000, 60);

四、創(chuàng)建與調(diào)用函數(shù)

4.1 創(chuàng)建函數(shù)

-- 場景:根據(jù)員工ID計算年薪(含獎金)
CREATE OR REPLACE FUNCTION calculate_annual_income (
    p_emp_id IN NUMBER,
    p_include_bonus IN BOOLEAN DEFAULT TRUE
) RETURN NUMBER
IS
    v_salary employees.salary%TYPE;
    v_commission employees.commission_pct%TYPE;
    v_annual_income NUMBER;
BEGIN
    -- 查詢薪水和提成比例
    SELECT salary, commission_pct
    INTO v_salary, v_commission
    FROM employees
    WHERE employee_id = p_emp_id;
    -- 計算年收入
    IF p_include_bonus AND v_commission IS NOT NULL THEN
        v_annual_income := v_salary * 12 * (1 + v_commission);
    ELSE
        v_annual_income := v_salary * 12;
    END IF;
    RETURN v_annual_income;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        RETURN NULL;  -- 函數(shù)必須返回值
    WHEN OTHERS THEN
        RETURN -1;    -- 錯誤標(biāo)識
END calculate_annual_income;
/

4.2 調(diào)用函數(shù)

-- 方式1:在 SQL 語句中調(diào)用(函數(shù)的核心優(yōu)勢)
SELECT 
    employee_id,
    first_name,
    salary,
    calculate_annual_income(employee_id, TRUE) AS annual_income
FROM employees
WHERE calculate_annual_income(employee_id) > 200000;
-- 方式2:在 PL/SQL 塊中調(diào)用
DECLARE
    v_income NUMBER;
BEGIN
    v_income := calculate_annual_income(101, FALSE);
    DBMS_OUTPUT.PUT_LINE('年收入:' || v_income);
END;
/
-- 方式3:在 WHERE 子句中調(diào)用
SELECT * FROM employees
WHERE calculate_annual_income(employee_id) > (SELECT AVG(salary*12) FROM employees);

五、高級特性

5.1 異常處理(Exception Handling)

-- 預(yù)定義異常
CREATE OR REPLACE PROCEDURE safe_delete_employee (
    p_emp_id IN NUMBER
) IS
BEGIN
    DELETE FROM employees WHERE employee_id = p_emp_id;
    IF SQL%NOTFOUND THEN
        RAISE_APPLICATION_ERROR(-20001, '員工 ' || p_emp_id || ' 不存在');
    END IF;
    COMMIT;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('沒有找到數(shù)據(jù)');
    WHEN TOO_MANY_ROWS THEN
        DBMS_OUTPUT.PUT_LINE('返回多行,但期望單行');
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('錯誤代碼:' || SQLCODE);
        DBMS_OUTPUT.PUT_LINE('錯誤消息:' || SQLERRM);
        ROLLBACK;
END;
/
-- 自定義異常
CREATE OR REPLACE PROCEDURE update_salary_check (
    p_emp_id IN NUMBER,
    p_new_salary IN NUMBER
) IS
    e_salary_too_high EXCEPTION;  -- 聲明自定義異常
    PRAGMA EXCEPTION_INIT(e_salary_too_high, -20002);  -- 關(guān)聯(lián)錯誤碼
BEGIN
    IF p_new_salary > 20000 THEN
        RAISE e_salary_too_high;  -- 拋出自定義異常
    END IF;
    UPDATE employees SET salary = p_new_salary WHERE employee_id = p_emp_id;
    COMMIT;
EXCEPTION
    WHEN e_salary_too_high THEN
        DBMS_OUTPUT.PUT_LINE('錯誤:新薪資不能超過20000');
        ROLLBACK;
END;
/

5.2 游標(biāo)(Cursor)

-- 顯式游標(biāo)處理多行數(shù)據(jù)
CREATE OR REPLACE PROCEDURE bulk_raise_salary (
    p_dept_id IN NUMBER,
    p_percent IN NUMBER
) IS
    -- 聲明游標(biāo)
    CURSOR emp_cursor IS
        SELECT employee_id, salary FROM employees 
        WHERE department_id = p_dept_id 
        FOR UPDATE;  -- 加鎖防止并發(fā)修改
    -- 記錄類型
    emp_rec emp_cursor%ROWTYPE;
BEGIN
    OPEN emp_cursor;
    LOOP
        FETCH emp_cursor INTO emp_rec;
        EXIT WHEN emp_cursor%NOTFOUND;  -- 退出循環(huán)條件
        -- 更新薪水
        UPDATE employees 
        SET salary = emp_rec.salary * (1 + p_percent/100)
        WHERE CURRENT OF emp_cursor;  -- 定位當(dāng)前游標(biāo)行
        DBMS_OUTPUT.PUT_LINE('員工 ' || emp_rec.employee_id || ' 已調(diào)整');
    END LOOP;
    CLOSE emp_cursor;
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        CLOSE emp_cursor;
        ROLLBACK;
        RAISE;
END;
/
-- 游標(biāo) FOR 循環(huán)(簡化)
CREATE OR REPLACE PROCEDURE process_high_earners IS
BEGIN
    FOR emp_rec IN (SELECT employee_id, salary FROM employees WHERE salary > 10000)
    LOOP
        INSERT INTO high_earner_log VALUES (emp_rec.employee_id, emp_rec.salary, SYSDATE);
    END LOOP;
    COMMIT;
END;
/

5.3 自治事務(wù)(Autonomous Transaction)

-- 日志記錄不受主事務(wù)影響
CREATE OR REPLACE PROCEDURE log_message (
    p_message IN VARCHAR2
) IS
    PRAGMA AUTONOMOUS_TRANSACTION;  -- 聲明自治事務(wù)
BEGIN
    INSERT INTO message_log (message, log_time) VALUES (p_message, SYSDATE);
    COMMIT;  -- 獨立提交,不影響主事務(wù)
END;
/
-- 主事務(wù)回滾,但日志已提交
CREATE OR REPLACE PROCEDURE main_transaction IS
BEGIN
    INSERT INTO orders VALUES (101, 5000);
    log_message('訂單 101 已創(chuàng)建');  -- 自治事務(wù)已提交
    ROLLBACK;  -- 訂單被回滾,但日志保留
END;
/

5.4 動態(tài) SQL(EXECUTE IMMEDIATE)

-- 場景:動態(tài)表名查詢
CREATE OR REPLACE FUNCTION dynamic_query (
    p_table_name IN VARCHAR2,
    p_id IN NUMBER
) RETURN VARCHAR2
IS
    v_sql VARCHAR2(1000);
    v_result VARCHAR2(100);
BEGIN
    v_sql := 'SELECT name FROM ' || p_table_name || ' WHERE id = :id';
    EXECUTE IMMEDIATE v_sql
    INTO v_result
    USING p_id;  -- 綁定變量防止 SQL 注入
    RETURN v_result;
EXCEPTION
    WHEN OTHERS THEN
        RETURN '查詢失?。? || SQLERRM;
END;
/
-- 動態(tài) DDL
CREATE OR REPLACE PROCEDURE create_log_table (p_table_name IN VARCHAR2) IS
BEGIN
    EXECUTE IMMEDIATE 'CREATE TABLE ' || p_table_name || '_log (
        id NUMBER GENERATED ALWAYS AS IDENTITY,
        message VARCHAR2(200),
        log_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )';
END;
/

六、包(Package)——代碼封裝

6.1 包的創(chuàng)建(規(guī)范 + 主體)

-- 1. 包規(guī)范(接口定義)
CREATE OR REPLACE PACKAGE employee_mgmt AS
    -- 常量
    c_max_salary CONSTANT NUMBER := 50000;
    -- 類型定義
    TYPE emp_rec_type IS RECORD (
        emp_id NUMBER,
        emp_name VARCHAR2(100),
        salary NUMBER
    );
    TYPE emp_tab_type IS TABLE OF emp_rec_type INDEX BY PLS_INTEGER;
    -- 函數(shù)聲明
    FUNCTION calculate_bonus (p_emp_id IN NUMBER) RETURN NUMBER;
    FUNCTION get_employee_info (p_emp_id IN NUMBER) RETURN emp_rec_type;
    -- 過程聲明
    PROCEDURE hire_employee (
        p_first_name IN VARCHAR2,
        p_last_name IN VARCHAR2,
        p_salary IN NUMBER
    );
    PROCEDURE fire_employee (p_emp_id IN NUMBER);
END employee_mgmt;
/
-- 2. 包主體(實現(xiàn))
CREATE OR REPLACE PACKAGE BODY employee_mgmt AS
    -- 私有函數(shù)(外部不可見)
    FUNCTION validate_salary (p_salary IN NUMBER) RETURN BOOLEAN IS
    BEGIN
        RETURN p_salary BETWEEN 1000 AND c_max_salary;
    END validate_salary;
    -- 公有函數(shù)實現(xiàn)
    FUNCTION calculate_bonus (p_emp_id IN NUMBER) RETURN NUMBER IS
        v_salary employees.salary%TYPE;
    BEGIN
        SELECT salary INTO v_salary FROM employees WHERE employee_id = p_emp_id;
        RETURN v_salary * 0.1;  -- 獎金為薪水的10%
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            RETURN 0;
    END calculate_bonus;
    -- 存儲過程實現(xiàn)
    PROCEDURE hire_employee (
        p_first_name IN VARCHAR2,
        p_last_name IN VARCHAR2,
        p_salary IN NUMBER
    ) IS
    BEGIN
        IF NOT validate_salary(p_salary) THEN
            RAISE_APPLICATION_ERROR(-20003, '薪資超出范圍');
        END IF;
        INSERT INTO employees (employee_id, first_name, last_name, salary, hire_date)
        VALUES (emp_seq.NEXTVAL, p_first_name, p_last_name, p_salary, SYSDATE);
        COMMIT;
    END hire_employee;
END employee_mgmt;
/

6.2 調(diào)用包內(nèi)程序

-- 調(diào)用包函數(shù)
SELECT employee_mgmt.calculate_bonus(101) FROM dual;
-- 調(diào)用包過程
DECLARE
    v_emp_info employee_mgmt.emp_rec_type;
BEGIN
    v_emp_info := employee_mgmt.get_employee_info(102);
    DBMS_OUTPUT.PUT_LINE('姓名:' || v_emp_info.emp_name);
    employee_mgmt.hire_employee('Alice', 'Smith', 7500);
END;
/

6.3 包的優(yōu)勢

  • 模塊化:邏輯分組,代碼組織清晰
  • 性能:首次加載后常駐內(nèi)存,后續(xù)調(diào)用更快
  • 封裝:公有/私有分離,隱藏實現(xiàn)細節(jié)
  • 狀態(tài)保持:包變量在會話中持續(xù)存在
  • 重載:支持同名過程/函數(shù)(參數(shù)不同)

七、性能優(yōu)化技巧

7.1 使用 BULK COLLECT 批量操作

-- 錯誤:逐行處理(慢)
CREATE OR REPLACE PROCEDURE slow_update IS
BEGIN
    FOR emp IN (SELECT employee_id, salary FROM employees WHERE department_id = 80)
    LOOP
        UPDATE employees SET salary = salary * 1.1 WHERE employee_id = emp.employee_id;
    END LOOP;
    COMMIT;
END;
-- 正確:批量處理
CREATE OR REPLACE PROCEDURE fast_update IS
    TYPE num_tab IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
    v_emp_ids num_tab;
    v_salaries num_tab;
BEGIN
    -- 批量獲取
    SELECT employee_id, salary 
    BULK COLLECT INTO v_emp_ids, v_salaries
    FROM employees WHERE department_id = 80;
    -- 批量更新
    FORALL i IN 1..v_emp_ids.COUNT
        UPDATE employees SET salary = v_salaries(i) * 1.1 
        WHERE employee_id = v_emp_ids(i);
    COMMIT;
END;

7.2 使用 NOCOPY 提示(減少參數(shù)復(fù)制開銷)

-- 對于大集合,使用 NOCOPY 避免拷貝
CREATE OR REPLACE PROCEDURE process_large_collection (
    p_collection IN OUT NOCOPY large_collection_type  -- NOCOPY 提示
) IS
BEGIN
    -- 直接操作原集合,不創(chuàng)建副本
    FOR i IN 1..p_collection.COUNT LOOP
        p_collection(i).status := 'PROCESSED';
    END LOOP;
END;

7.3 避免上下文切換

-- 錯誤:SQL 和 PL/SQL 頻繁切換
CREATE OR REPLACE FUNCTION get_department_name (p_dept_id NUMBER) RETURN VARCHAR2 IS
    v_name VARCHAR2(100);
BEGIN
    SELECT department_name INTO v_name FROM departments WHERE department_id = p_dept_id;
    RETURN v_name;
END;
-- 查詢時使用
SELECT employee_id, get_department_name(department_id) FROM employees;  -- 低效
-- 正確:純 SQL 實現(xiàn)
SELECT e.employee_id, d.department_name
FROM employees e JOIN departments d ON e.department_id = d.department_id;

八、調(diào)試與監(jiān)控

8.1 DBMS_OUTPUT 調(diào)試

SET SERVEROUTPUT ON;  -- 開啟輸出
CREATE OR REPLACE PROCEDURE debug_demo IS
    v_counter NUMBER := 0;
BEGIN
    FOR rec IN (SELECT employee_id FROM employees WHERE ROWNUM <= 5)
    LOOP
        v_counter := v_counter + 1;
        DBMS_OUTPUT.PUT_LINE('處理第 ' || v_counter || ' 個員工:' || rec.employee_id);
    END LOOP;
END;
/

8.2 使用 DBMS_APPLICATION_INFO

-- 在 V$SESSION 中顯示進度
CREATE OR REPLACE PROCEDURE long_running_task IS
    v_total NUMBER;
BEGIN
    SELECT COUNT(*) INTO v_total FROM employees;
    FOR rec IN (SELECT employee_id FROM employees)
    LOOP
        DBMS_APPLICATION_INFO.SET_MODULE(
            module_name => 'SALARY_UPDATE',
            action_name => 'Processing ' || rec.employee_id
        );
        DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS(
            rindex => DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS_NOHINT,
            slno => 0,
            op_name => 'Employee Processing',
            sofar => rec.employee_id,
            totalwork => v_total,
            units => 'employees'
        );
        -- 業(yè)務(wù)邏輯
        UPDATE employees SET salary = salary * 1.05 WHERE employee_id = rec.employee_id;
    END LOOP;
END;
/
-- 監(jiān)控查詢
SELECT sid, serial#, module, action FROM v$session WHERE module = 'SALARY_UPDATE';
SELECT * FROM v$session_longops WHERE opname = 'Employee Processing';

8.3 依賴關(guān)系查詢

-- 查看存儲過程依賴的表
SELECT referenced_owner, referenced_name, referenced_type
FROM all_dependencies
WHERE owner = 'HR' 
AND name = 'ADJUST_EMPLOYEE_SALARY'
AND type = 'PROCEDURE'
ORDER BY referenced_type;
-- 查看哪些對象依賴該過程
SELECT name, type
FROM all_dependencies
WHERE referenced_owner = 'HR'
AND referenced_name = 'ADJUST_EMPLOYEE_SALARY';

九、最佳實踐與規(guī)范

9.1 命名規(guī)范

-- 前綴規(guī)范
- 存儲過程:p_業(yè)務(wù)模塊_操作(如 p_emp_update_salary)
- 函數(shù):f_業(yè)務(wù)模塊_計算(如 f_emp_calc_bonus)
- 包:pkg_業(yè)務(wù)模塊(如 pkg_employee_mgmt)
- 參數(shù):p_參數(shù)名(輸入)、p_參數(shù)名_out(輸出)、p_參數(shù)名_io(輸入輸出)
- 變量:v_變量名(局部)、g_變量名(全局包變量)

9.2 編碼規(guī)范

-- 1. 總是使用 AUTHID 明確權(quán)限
CREATE OR REPLACE PROCEDURE secure_proc(...) IS
    AUTHID CURRENT_USER  -- 調(diào)用者權(quán)限
IS
BEGIN
    ...
END;
/
-- 2. 參數(shù)使用 %TYPE 錨定
CREATE OR REPLACE PROCEDURE update_emp (
    p_emp_id IN employees.employee_id%TYPE,  -- 類型自動同步
    p_salary IN employees.salary%TYPE
) IS ...
-- 3. 使用顯式游標(biāo)而非隱式
-- 錯誤:隱式游標(biāo)無法處理 NO_DATA_FOUND
SELECT ... INTO ...;  -- 不推薦
-- 正確:顯式游標(biāo)控制
DECLARE
    CURSOR c_emp IS SELECT ...;
BEGIN
    OPEN c_emp;
    LOOP
        FETCH c_emp INTO ...;
        EXIT WHEN c_emp%NOTFOUND;
    END LOOP;
    CLOSE c_emp;
END;
-- 4. 異常處理精細化
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        -- 處理查詢不到數(shù)據(jù)
    WHEN DUP_VAL_ON_INDEX THEN
        -- 處理唯一鍵沖突
    WHEN OTHERS THEN
        -- 記錄錯誤日志后重新拋出
        log_error(SQLCODE, SQLERRM);
        RAISE;  -- 重新拋出,讓上層調(diào)用者處理

9.3 性能黃金法則

? 批量操作替代逐行處理(FORALL)
? 避免游標(biāo)循環(huán)中的 SQL(先 JOIN 再處理)
? 使用 NOCOPY 減少大集合拷貝
? SQL 能做的事不要放在 PL/SQL 中
? 使用 DBMS_PROFILER 定位性能瓶頸
? 避免在函數(shù)中執(zhí)行 DML(SQL 調(diào)用時會導(dǎo)致上下文切換)
? 避免過度使用自治事務(wù)(破壞事務(wù)原子性)

十、總結(jié)對比

存儲過程 vs 函數(shù)終極對比

維度存儲過程函數(shù)
返回值0 或多個(OUT 參數(shù))必須 1 個(RETURN)
SQL 調(diào)用? 不可? 可在 SELECT/ WHERE 中調(diào)用
事務(wù)控制? 可 COMMIT/ROLLBACK? 應(yīng)避免(非確定性)
副作用? 可修改數(shù)據(jù)?? 應(yīng)保持純計算
性能適合復(fù)雜業(yè)務(wù)邏輯適合計算和轉(zhuǎn)換
調(diào)試較難(無 RETURN)較易(可單元測試)
使用場景批處理、ETL、API 封裝公式、驗證、數(shù)據(jù)轉(zhuǎn)換

選擇原則

  • 需要返回多個值 → 存儲過程
  • 需要在 SQL 中使用 → 函數(shù)
  • 需要修改數(shù)據(jù) → 存儲過程(函數(shù)也可但應(yīng)避免)
  • 純計算邏輯 → 函數(shù)(保持確定性)

掌握存儲過程和函數(shù),是 Oracle 后端開發(fā)的核心技能。它們能將業(yè)務(wù)邏輯下沉到數(shù)據(jù)庫層,提升性能、安全性和可維護性。

到此這篇關(guān)于Oracle數(shù)據(jù)庫PL/SQL 存儲過程與函數(shù)完全指南的文章就介紹到這了,更多相關(guān)oracle pl/sql存儲過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

鄢陵县| 集安市| 太原市| 澄江县| 岐山县| 体育| 铅山县| 涿鹿县| 焉耆| 杭锦后旗| 夏河县| 东阿县| 巫山县| 大庆市| 夏邑县| 延庆县| 平舆县| 隆安县| 屏山县| 恩施市| 新津县| 霍山县| 宝应县| 牙克石市| 汉阴县| 富宁县| 乡城县| 闽清县| 光山县| 通道| 威远县| 砀山县| 昔阳县| 城固县| 江永县| 彭泽县| 关岭| 古蔺县| 阿克| 蓝田县| 顺昌县|