定義新存儲過程。

語法

CREATE [OR REPLACE] PROCEDURE name [ (parameters) ]
   [
          IMMUTABLE
        | STABLE
        | VOLATILE
        | DETERMINISTIC
        | [ NOT ] LEAKPROOF
        | CALLED ON NULL INPUT
        | RETURNS NULL ON NULL INPUT
        | STRICT
        | [ EXTERNAL ] SECURITY INVOKER
        | [ EXTERNAL ] SECURITY DEFINER
        | AUTHID DEFINER
        | AUTHID CURRENT_USER
        | PARALLEL { UNSAFE | RESTRICTED | SAFE }
        | COST execution_cost
        | ROWS result_rows
        | SET configuration_parameter
          { TO value | = value | FROM CURRENT }
   ...]
{ IS | AS }
    [ PRAGMA AUTONOMOUS_TRANSACTION; ]
    [ declarations ]
  BEGIN
    statements
  END [ name ];

說明

CREATE PROCEDURE 可定義新存儲過程。CREATE OR REPLACE PROCEDURE 將創建新的存儲過程或替換現有定義。

如果包括 schema 名稱,則在指定的 schema 中創建存儲過程。否則在當前 schema 中創建。新存儲過程的名稱不得與同一 schema 中具有相同輸入參數類型的任何現有存儲過程匹配。不過,具有不同輸入參數類型的存儲過程可共用一個名稱(這稱為重載)。(存儲過程重載是一項 PolarDB PostgreSQL版(兼容Oracle)功能 - 獨立存儲過程的重載與 Oracle 數據庫不兼容。)

要更新現有存儲過程的定義,可使用 CREATE OR REPLACE PROCEDURE。無法以這種方式更改存儲過程的名稱或參數類型(如果您嘗試過,實際上創建的是一個新的不同存儲過程)。使用 OUT 參數時,除非通過刪除存儲過程,否則不能更改任何 OUT 參數的類型。

參數

參數說明
namename 是存儲過程的標識符。
parametersparameters 是形參的列表。
declarationsdeclarations 是變量、游標、類型或子程序聲明。如果包括子程序聲明,則它們必須在所有其他變量、游標和類型聲明之后。
statementsstatements 是 SPL 程序語句(BEGIN - END 塊可以包含 EXCEPTION 部分)。
IMMUTABLE | STABLE | VOLATILE這些屬性將存儲過程的行為通知給查詢優化器;您只能指定一個選項。VOLATILE 是默認行為。
  • IMMUTABLE 指示存儲過程不能修改數據庫,并在提供相同參數值時始終會得到相同結果;它不執行數據庫查找,也不以其他方式使用其參數列表中不直接存在的信息。如果包括此子句,則使用全常量參數對存儲過程的任何調用將立即替換為存儲過程值。
  • STABLE 指示該存儲過程不能修改數據庫,并且在單表掃描中,它將始終為相同的參數值返回相同的結果,但其結果可能會因 SQL 語句而變化。對于依賴于數據庫查找、參數變量(例如當前時區)等的存儲過程,這是合適的選擇。
  • VOLATILE 指示即使在單表掃描中存儲過程值也可以更改,因此不能進行任何優化。請注意,任何具有負面影響的函數必須分類為易失性函數,即使其結果可預測性很好也是如此,這是為了防止調用由于優化而被去除。
DETERMINISTICDETERMINISTIC 是 IMMUTABLE 的同義詞。DETERMINISTIC 存儲過程不能修改數據庫,并在提供相同參數值時始終會得到相同結果;它不執行數據庫查找,也不以其他方式使用其參數列表中不直接存在的信息。如果包括此子句,則使用全常量參數對存儲過程的任何調用將立即替換為存儲過程值。
[ NOT ] LEAKPROOFLEAKPROOF 存儲過程沒有負面影響,也不會公開有關調用存儲過程所用值的任何信息。
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
  • CALLED ON NULL INPUT(默認值)指示當存儲過程的某些參數為 NULL 時,將正常調用該存儲過程。如果需要,作者需要負責檢查 NULL 值并做出適當的響應。
  • RETURNS NULL ON NULL INPUT 或 STRICT 指示只要存儲過程的任何參數為 NULL,該存儲過程就始終返回 NULL。如果指定了這些子句,則當存在 NULL 參數時,不會執行該存儲過程,而是自動假定為 NULL 結果。
[ EXTERNAL ] SECURITY DEFINERSECURITY DEFINER 指定存儲過程將使用創建它的用戶的特權來執行。這是默認值。為了符合 SQL 要求,允許使用關鍵字 EXTERNAL,但這是可選的。
[ EXTERNAL ] SECURITY INVOKERSECURITY INVOKER 子句指示存儲過程將使用調用它的用戶的特權執行。為了符合 SQL 要求,允許使用關鍵字 EXTERNAL,但這是可選的。
AUTHID DEFINER | AUTHID CURRENT_USER
  • AUTHID DEFINER 子句是 [EXTERNAL] SECURITY DEFINER 的同義詞。如果省略 AUTHID 子句,或指定 AUTHID DEFINER,則將使用存儲過程所有者的權限來確定對數據庫對象的訪問特權。
  • AUTHID CURRENT_USER 子句是 [EXTERNAL] SECURITY INVOKER 的同義詞。如果指定 AUTHID CURRENT_USER,則將使用執行存儲過程的當前用戶的權限來確定訪問特權。
PARALLEL { UNSAFE | RESTRICTED | SAFE }通過 PARALLEL 子句可以使用并行順序掃描(并行模式)。在查詢期間,相比串行順序掃描,并行順序掃描使用多個工作線程并行掃描一個關系。
  • 如果設置為 UNSAFE,則該存儲過程不能以并行模式執行。存在此類存儲過程時,會強制執行串行執行計劃。如果省略 PARALLEL 子句,則這是默認設置。
  • 如果設置為 RESTRICTED,則該存儲過程可以在并行模式下執行,但執行限制為并行組中的前幾個。如果任何特定關系的限定條件具有存在并行限制的任何內容,則不會為并行執行選擇該關系。
  • 如果設置為 SAFE,則該存儲過程可以在并行模式下執行,而沒有任何限制。
COST execution_costexecution_cost 是一個正數,給出該存儲過程的估計執行成本,單位為 cpu_operator_cost。如果存儲過程返回一個集合,則這是每個返回行的成本。較大值會導致計劃程序嘗試避免超出必要的頻率來對函數求值。
ROWS result_rowsresult_rows 是一個正數,給出計劃程序預計存儲過程返回的估計行數。僅當存儲過程聲明為返回一個集合時,才允許這么做。默認假定值為 1000 行。
SET configuration_parameter { TO value | = value | FROM CURRENT }SET 子句使指定的配置參數在進入存儲過程時設置為指定值,然后在存儲過程退出時恢復為其之前的值。SET FROM CURRENT 將會話的當前參數值保存為進入存儲過程時要應用的值。

如果將 SET 子句附加到存儲過程,則在存儲過程內針對相同變量執行 SET LOCAL 命令的效果僅限于該存儲過程。存儲過程退出時配置參數將恢復為之前的值。普通的 SET 命令(沒有 LOCAL)會重寫 SET 子句,與對之前 SET LOCAL 命令的操作很相似,此命令的效果在退出存儲過程后會保留,除非回滾當前事務。

PRAGMA AUTONOMOUS_TRANSACTIONPRAGMA AUTONOMOUS_TRANSACTION 是將存儲過程設置為自治事務的指令。
說明
  • STRICT、LEAKPROOF、PARALLEL、COST、ROWS 和 SET 關鍵字可以為PolarDB PostgreSQL版(兼容Oracle)提供擴展功能,但 Oracle 不支持這些關鍵字。
  • 只有PolarDB PostgreSQL版(兼容Oracle)存儲過程支持 IMMUTABLE、STABLE、STRICT、LEAKPROOF、COST、ROWS 及 PARALLEL { UNSAFE | RESTRICTED | SAFE } 屬性。
  • 默認情況下存儲過程創建為 SECURITY DEFINERS。在 plpgsql 中定義的存儲過程創建為 SECURITY INVOKERS。

示例

以下存儲過程列出了 emp 表中的員工:

CREATE OR REPLACE PROCEDURE list_emp
IS
    v_empno         NUMBER(4);
    v_ename         VARCHAR2(10);
    CURSOR emp_cur IS
        SELECT empno, ename FROM emp ORDER BY empno;
BEGIN
    OPEN emp_cur;
    DBMS_OUTPUT.PUT_LINE('EMPNO    ENAME');
    DBMS_OUTPUT.PUT_LINE('-----    -------');
    LOOP
        FETCH emp_cur INTO v_empno, v_ename;
        EXIT WHEN emp_cur%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(v_empno || '     ' || v_ename);
    END LOOP;
    CLOSE emp_cur;
END;

EXEC list_emp;

EMPNO    ENAME
-----    -------
7369     SMITH
7499     ALLEN
7521     WARD
7566     JONES
7654     MARTIN
7698     BLAKE
7782     CLARK
7788     SCOTT
7839     KING
7844     TURNER
7876     ADAMS
7900     JAMES
7902     FORD
7934     MILLER

以下存儲過程使用 IN OUT 和 OUT 參數返回員工的編號、姓名和職位,首先采用給定的員工編號進行搜索,如果找不到則采用給定的姓名。一個匿名塊調用該存儲過程。

CREATE OR REPLACE PROCEDURE emp_job (
    p_empno         IN OUT emp.empno%TYPE,
    p_ename         IN OUT emp.ename%TYPE,
    p_job           OUT    emp.job%TYPE
)
IS
    v_empno         emp.empno%TYPE;
    v_ename         emp.ename%TYPE;
    v_job           emp.job%TYPE;
BEGIN
    SELECT ename, job INTO v_ename, v_job FROM emp WHERE empno = p_empno;
    p_ename := v_ename;
    p_job   := v_job;
    DBMS_OUTPUT.PUT_LINE('Found employee # ' || p_empno);
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        BEGIN
            SELECT empno, job INTO v_empno, v_job FROM emp
                WHERE ename = p_ename;
            p_empno := v_empno;
            p_job   := v_job;
            DBMS_OUTPUT.PUT_LINE('Found employee ' || p_ename);
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                DBMS_OUTPUT.PUT_LINE('Could not find an employee with ' ||
                    'number, ' || p_empno || ' nor name, '  || p_ename);
                p_empno := NULL;
                p_ename := NULL;
                p_job   := NULL;
        END;
END;

DECLARE
    v_empno      emp.empno%TYPE;
    v_ename      emp.ename%TYPE;
    v_job        emp.job%TYPE;
BEGIN
    v_empno := 0;
    v_ename := 'CLARK';
    emp_job(v_empno, v_ename, v_job);
    DBMS_OUTPUT.PUT_LINE('Employee No: ' || v_empno);
    DBMS_OUTPUT.PUT_LINE('Name       : ' || v_ename);
    DBMS_OUTPUT.PUT_LINE('Job        : ' || v_job);
END;

Found employee CLARK
Employee No: 7782
Name       : CLARK
Job        : MANAGER

以下示例演示了如何在存儲過程聲明中使用 AUTHID DEFINER 和 SET 子句。update_salary 存儲過程將定義了該存儲過程的角色的特權傳遞給正在調用該存儲過程的角色(在執行該存儲過程時):

CREATE OR REPLACE PROCEDURE update_salary(id INT, new_salary NUMBER)
  SET SEARCH_PATH = 'public' SET WORK_MEM = '1MB'
  AUTHID DEFINER IS
BEGIN
  UPDATE emp SET salary = new_salary WHERE emp_id = id;
END;

包括 SET 子句,將存儲過程的搜索路徑設置為 public,并將工作內存設置為 1MB。其他存儲過程、函數及對象不受這些設置的影響。

在此示例中,AUTHID DEFINER 子句將特權臨時授予可能不允許執行存儲過程內語句的角色。要指示服務器使用與調用存儲過程的角色相關聯的特權,可將 AUTHID DEFINER 子句替換為 AUTHID CURRENT_USER 子句。