PostgreSql詳細介紹(六)

PostgreSQL 學習手冊(SQL 語言函數) 
一、基本概念: 
 
    SQL 函數可以包含任意數量的查詢,但是函數只返回最後一個查詢(必須是 SELECT)的結果。在簡單情況下,返回最後一條查詢 結果的第一行。如果最後一個查詢不返回任何行,那麼該函數將返回 NULL 值。如果需要該函數返回最後一條 SELECT 語句的所有行, 可以將函數的返回值定義爲集合,即 SETOF sometype。

    SQL 函數的函數體應該是用分號分隔的 SQL 語句列表,其中最後一條語句之後的分號是可選的。除非函數聲明爲返回 void,否 則最後一條語句必須是 SELECT。事實上,在 SQL 函數中,不僅可以包含 SELECT 查詢語句,也可以包含 INSERT、UPDATE 和 DELETE 等其他標準的 SQL 語句,但是和事物相關的語句不能包含其中,如 BEGIN、COMMIT、ROLLBACK 和 SAVEPOINT 等。
     CREATE FUNCTION 命令的語法要求函數體寫成一個字符串文本。通常來說,該文本字符串常量使用美元符($$)圍住,如:     CREATE FUNCTION clean_emp() RETURNS void AS $$

        DELETE FROM emp WHERE salary < 0;

    $$ LANGUAGE SQL;

    最後需要說明的是 SQL 函數中的參數,PostgreSQL 定義$1 表示第一個參數,$2 爲第二個參數並以此類推。如果參數是複合類 型,則可以使用點表示法,即$1.name 訪問複合類型參數中的 name 字段。需要注意的是函數參數只能用作數據值,而不能用於標 識符,如:

    INSERT INTO mytable VALUES ($1);   -合法
     INSERT INTO $1 VALUES (42);          -不合法 ( 表 名屬於標示符之一 )      
二、基本類型: 
         最簡單的 SQL 函數可能就是沒有參數且返回基本類型的函數了,如:

    CREATE FUNCTION one() RETURNS integer AS $$  

      SELECT 1 AS result;

    $$ LANGUAGE SQL;  

  下面的例子聲明瞭基本類型作爲函數的參數。

    CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$

        SELECT $1 + $2;

    $$ LANGUAGE SQL;  

  # 通過 select 調用函數。
     postgres=# SELECT add_em(1,2) AS answer;

     answer

    --------  

        3  

  (1 row)

            在下面的例子中,函數體內包含多個 SQL 語句,它們之間是用分號進行分隔的。

    CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$

        UPDATE bank SET balance = balance - $2 WHERE accountno = $1;

        SELECT balance FROM bank WHERE accountno = $1;

    $$ LANGUAGE SQL;      
三、複合類型: 
     見如下示例: 
    1). 創建數據表,這樣與之對應的複合類型也隨之生成。

    CREATE TABLE emp (

        name        text,

        salary      numeric,

        age         integer

   );

    2). 創建函數,其參數爲複合類型。在函數體內,可以像引用基本類型參數那樣引用複合類型,如$1。訪問複合類型的字段使用 點表達式即可,如:$1.salary。

    CREATE FUNCTION double_salary(emp) RETURNS integer AS $$  

      SELECT ($1.salary * 2)::integer AS salary;     $$ LANGUAGE SQL;  

  3). 在 select 語句中,可以使用 emp.*表示 emp 表的一整行數據。

    SELECT name, double_salary(emp.*) AS dream FROM emp WHERE age > 30;

    4). 我們也可以使用 ROW 表達式構造自定義的複合類型,如:

    SELECT name, double_salary(ROW(name, salary*1.1, age)) AS dream FROM emp;  

  5). 創建一個函數,其返回值爲複合類型,如:

    CREATE FUNCTION new_emp() RETURNS emp AS $$

        SELECT ROW('None', 1000.0, 25)::emp;     $$ LANGUAGE SQL;

    6). 調用返回複合類型的函數。

    SELECT new_emp();

    7). 調用返回複合類型的函數,同時訪問該返回值的某個字段。

    SELECT (new_emp()).name; 
 
四、帶輸出參數的函數: 
 
    還有一種方法可以用於返回函數執行的結果,即輸出參數,如:

    CREATE FUNCTION add_em2 (IN x int, IN y int, OUT sum int) AS $$

        SELECT $1 + $2

    $$ LANGUAGE SQL;

    調用方法和返回結果與 add_em(帶有返回值的函數)完全一致,如:

    SELECT add_em(3,7);  

       這個帶有輸出參數的函數和之前的 add_em 函數沒有本質的區別。事實上,輸出參數的真正價值在於它爲函數提供了返回多個字 段的途徑。如,

    CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int) AS $$

        SELECT $1 + $2, $1 * $2

    $$ LANGUAGE SQL;

    調用方式沒有改變,只是返回結果多出一列。

    SELECT * FROM sum_n_product(11,42);

     sum | product  

  -----+---------  

    53 |     462    

(1 row)    

在上面的示例中,IN 用於表示該函數參數爲輸入參數(缺省值,可以忽略),OUT 則表示該參數爲輸出參數。 
 
五、返回結果作爲表數據源:

     所有 SQL 函數都可以在查詢的 FROM 子句裏使用。該方法對於返回複合類型的函數而言特別有用,如果該函數定義爲返回一個基 本類型,那麼該函數生成一個單字段表,如果該函數定義爲返回一個複合類型,那麼該函數生成一個複合類型裏每個屬性組成的行。 見如下示例:

    1). 創建一個數據表。

    CREATE TABLE foo (  

      fooid    int,

         foosubid int,  

       fooname  text  

  );  

  2). 創建 SQL 函數,其返回值爲與 foo 表對應的複合類型。

    CREATE FUNCTION getfoo(int) RETURNS foo AS $$

        SELECT * FROM foo WHERE fooid = $1;

    $$ LANGUAGE SQL;

    3). 在 FROM 子句中調用該函數。

    SELECT *, upper(fooname) FROM getfoo(1) AS t1;      
六、返回集合的 SQL 函數: 
 
    如果 SQL 函數的返回值爲 SETOF sometype,那麼在調用該函數時,將返回最後一個 SELECT 查詢的全部數據。這個特性通常 用於把函數放在 FROM 子句裏調用,見如下示例:

    CREATE FUNCTION getfoo(int) RETURNS setof foo AS $$  

      SELECT * FROM foo WHERE fooid = $1;  

  $$ LANGUAGE SQL;

在 FROM 子句中調用了返回複合類型集合的函數,其結果等同於:SELECT * FROM (SELECT * FROM foo WHERE fooid = 1) t1;     SELECT * FROM getfoo(1) AS t1;          
七、多態的 SQL 函數: 
 
    SQL 函數可以聲明爲接受多態類型(anyelement 和 anyarray)的參數或返回多態類型的返回值,見如下示例:

    1). 函數參數和返回值均爲多態類型。

    CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$

        SELECT ARRAY[$1, $2];

    $$ LANGUAGE SQL;

    其調用方式和調用其它類型的 SQL 函數完全相同,只是在傳遞字符串類型的參數時,需要顯式轉換到目標類型,否則將會被視爲 unknown 類型,如:

    SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;

    2). 函數的參數爲多態類型,而返回值則爲基本類型。

    CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$  

      SELECT $1 > $2;

    $$ LANGUAGE SQL;  

  3). 多態類型用於函數的輸出參數。

    CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray) AS $$

        SELECT $1, ARRAY[$1,$1]

    $$ LANGUAGE sql; 

八、函數重載: 
 
    多個函數可以定義成相同的函數名,但是它們的參數一定要有所區分。換句話說,函數名可以重載,此規則有些類似於面向對象語 言中的函數重載,見如下示例:

    CREATE FUNCTION test(int, real) RETURNS ...     CREATE FUNCTION test(smallint, double) RETURNS ... 
 
    由於在 PostgreSQL 中函數支持重載,因此在刪除函數時,也必須指定參數列表,如:

    DROP FUNCTION test(int, real);     DROP FUNCTION test(smallint,double);  
PostgreSQL 學習手冊(PL/pgSQL 過程語言) 
一、概述: 
 
    PL/pgSQL 函數在第一次被調用時,其函數內的源代碼(文本)將被解析爲二進制指令樹,但是函數內的表達式和 SQL 命令只有在 首次用到它們的時候,PL/pgSQL 解釋器纔會爲其創建一個準備好的執行規劃,隨後對該表達式或 SQL 命令的訪問都將使用該規劃。 如果在一個條件語句中,有部分 SQL 命令或表達式沒有被用到,那麼 PL/pgSQL 解釋器在本次調用中將不會爲其準備執行規劃,這 樣的好處是可以有效地減少爲 PL/pgSQL 函數裏的語句生成分析和執行規劃的總時間,然而缺點是某些表達式或 SQL 命令中的錯誤 只有在其被執行到的時候才能發現。

    由於 PL/pgSQL 在函數裏爲一個命令制定了執行計劃,那麼在本次會話中該計劃將會被反覆使用,這樣做往往可以得到更好的性 能,但是如果你動態修改了相關的數據庫對象,那麼就有可能產生問題,如:

    CREATE FUNCTION populate() RETURNS integer AS $$

    DECLARE

        -- 聲明段

    BEGIN

        PERFORM my_function();

    END;  

  $$ LANGUAGE plpgsql;

    在調用以上函數時,PERFORM 語句的執行計劃將引用 my_function 對象的 OID。在此之後,如果你重建了 my_function 函數, 那麼 populate 函數將無法再找到原有 my_function 函數的 OID。要解決該問題,可以選擇重建 populate 函數,或者重新登錄建 立新的會話,以使 PostgreSQL 重新編譯該函數。要想規避此類問題的發生,在重建 my_function 時可以使用 CREATE OR REPLACE FUNCTION 命令。

    鑑於以上規則,在 PL/pgSQL 裏直接出現的 SQL 命令必須在每次執行時均引用相同的表和字段,換句話說,不能將函數的參數用 作 SQL 命令的表名或字段名。如果想繞開該限制,可以考慮使用 PL/pgSQL 中的 EXECUTE 語句動態地構造命令,由此換來的代價 是每次執行時都要構造一個新的命令計劃。

    使用 PL/pgSQL 函數的一個非常重要的優勢是可以提高程序的執行效率,由於原有的 SQL 調用不得不在客戶端與服務器之間反覆 傳遞數據,這樣不僅增加了進程間通訊所產生的開銷,而且也會大大增加網絡 IO 的開銷。 
 
二、PL/pgSQL 的結構: 
 
    PL/pgSQL 是一種塊結構語言,函數定義的所有文本都必須在一個塊內,其中塊中的每個聲明和每條語句都是以分號結束,如果 某一子塊在另外一個塊內,那麼該子塊的 END 關鍵字後面必須以分號結束,不過對於函數體的最後一個 END 關鍵字,分號可以省略, 如: 
    [ <<label>> ]

    [ DECLARE declarations ]

    BEGIN

        statements

    END [ label ];

    在 PL/pgSQL 中有兩種註釋類型,雙破折號(--)表示單行註釋。/* */表示多行註釋,該註釋類型的規則等同於 C 語言中的多行 註釋。

    在語句塊前面的聲明段中定義的變量在每次進入語句塊(BEGIN)時都會將聲明的變量初始化爲它們的缺省值,而不是每次函數調 用時初始化一次。如:

    CREATE FUNCTION somefunc() RETURNS integer AS $$

    DECLARE

       quantity integer := 30;

    BEGIN

       RAISE NOTICE 'Quantity here is %', quantity;  

   -在這裏的數量是 30  

     quantity := 50;  

     --  

     -- 創建一個子塊

       --

       DECLARE  

        quantity integer := 80;

       BEGIN

          RAISE NOTICE 'Quantity here is %', quantity;   -在這裏的數量是 80

       END;

       RAISE NOTICE 'Quantity here is %', quantity;

     -在這裏的數量是 50

           RETURN quantity;

    END;

    $$ LANGUAGE plpgsql;

    # 執行該函數以進一步觀 察其執行的結果。
     postgres=# select somefunc();

    NOTICE:  Quantity here is 30

    NOTICE:  Quantity here is 80

    NOTICE:  Quantity here is 50

     somefunc

    ----------  

         50    

(1 row)

    最後需要說明的是,目前版本的 PostgreSQL 並不支持嵌套事務,函數中的事物總是由外層命令(函數的調用者)來控制的,它們 本身無法開始或提交事務。 
 
三、聲明: 
 
    所有在塊裏使用的變量都必須在塊的聲明段裏先進行聲明,唯一的例外是 FOR 循環裏的循環計數變量,該變量被自動聲明爲整型。 變量聲明的語法如下:

    variable_name [ CONSTANT ] variable_type [ NOT NULL ] [ { DEFAULT | := } expression ];  

  1). SQL 中的數據類型均可作爲 PL/pgSQL 變量的數據類型,如 integer、varchar 和 char 等。

    2). 如果給出了 DEFAULT 子句,該變量在進入 BEGIN 塊時將被初始化爲該缺省值,否則被初始化爲 SQL 空值。缺省值是在每 次進入該塊時進行計算的。因此,如果把 now()賦予一個類型爲 timestamp 的變量,那麼該變量的缺省值將爲函數實際調用時的時 間,而不是函數預編譯時的時間。 
    3). CONSTANT 選項是爲了避免該變量在進入 BEGIN 塊後被重新賦值,以保證該變量爲常量。

    4). 如果聲明瞭 NOT NULL,那麼賦予 NULL 數值給該變量將導致一個運行時錯誤。因此所有聲明爲 NOT NULL 的變量也必須在 聲明時定義一個非空的缺省值。 
 
    1. 函數參數的別名:

    傳遞給函數的參數都是用$1、$2 這樣的標識符來表示的。爲了增加可讀性,我們可以爲其聲明別名。之後別名和數字標識符均可 指向該參數值,見如下示例:

    1). 在函數聲明的同時給出參數變量名。

    CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$

     BEGIN

        RETURN subtotal * 0.06;

    END;  

  $$ LANGUAGE plpgsql;

    2). 在聲明段中爲參數變量定義別名。

    CREATE FUNCTION sales_tax(REAL) RETURNS real AS $$

    DECLARE

        subtotal ALIAS FOR $1;  

  BEGIN

        RETURN subtotal * 0.06;

    END;  

  $$ LANGUAGE plpgsql;

    3). 對於輸出參數而言,我們仍然可以遵守 1)和 2)中的規則。

    CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$  

  BEGIN

        tax := subtotal * 0.06;

    END;

    $$ LANGUAGE plpgsql;

        4). 如果 PL/pgSQL 函數的返回類型爲多態類型(anyelement 或 anyarray),那麼函數就會創建一個特殊的參數:$0。我們仍 然可以爲該變量設置別名。

    CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)

    RETURNS anyelement AS $$

    DECLARE

        result ALIAS FOR $0;

    BEGIN

        result := v1 + v2 + v3;

        RETURN result;

    END;

    $$ LANGUAGE plpgsql;

          2. 拷貝類型:

    見如下形式的變量聲明:

    variable%TYPE

    %TYPE 表示一個變量或表字段的數據類型,PL/pgSQL 允許通過該方式聲明一個變量,其類型等同於 variable 或表字段的數據 類型,見如下示例:

    user_id users.user_id%TYPE;

    在上面的例子中,變量 user_id 的數據類型等同於 users 表中 user_id 字段的類型。

    通過使用%TYPE,一旦引用的變量類型今後發生改變,我們也無需修改該變量的類型聲明。最後需要說明的是,我們可以在函數 的參數和返回值中使用該方式的類型聲明。 

    3. 行類型:

    見如下形式的變量聲明:

    name table_name%ROWTYPE;

    name composite_type_name;

    table_name%ROWTYPE 表示指定表的行類型,我們在創建一個表的時候,PostgreSQL 也會隨之創建出一個與之相應的複合 類型,該類型名等同於表名,因此,我們可以通過以上兩種方式來聲明行類型的變量。由此方式聲明的變量,可以保存 SELECT 返回 結果中的一行。如果要訪問變量中的某個域字段,可以使用點表示法,如 rowvar.field,但是行類型的變量只能訪問自定義字段,無 法訪問系統提供的隱含字段,如 OID 等。對於函數的參數,我們只能使用複合類型標識變量的數據類型。最後需要說明的是,推薦使 用%ROWTYPE 的聲明方式,這樣可以具有更好的可移植性,因爲在 Oracle 的 PL/SQL 中也存在相同的概念,其聲明方式也 爲%ROWTYPE。見如下示例:

    CREATE FUNCTION merge_fields(t_row table1) RETURNS text AS $$

    DECLARE

        t2_row table2%ROWTYPE;

    BEGIN

        SELECT * INTO t2_row FROM table2 WHERE id = 1 limit 1;

        RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7;

    END;

    $$ LANGUAGE plpgsql; 
 
    4. 記錄類型:

    見如下形式的變量聲明:

    name RECORD;

    記錄變量類似於行類型變量,但是它們沒有預定義的結構,只能通過 SELECT 或 FOR 命令來獲取實際的行結構,因此記錄變量在 被初始化之前無法訪問,否則將引發運行時錯誤。

    注:RECORD 不是真正的數據類型,只是一個佔位符。 
 
四、基本語句: 
 
    1. 賦值:

    PL/pgSQL 中賦值語句的形式爲:identIFier := expression,等號兩端的變量和表達式的類型或者一致,或者可以通過 PostgreSQL 的轉換規則進行轉換,否則將會導致運行時錯誤,見如下示例:

    user_id := 20;

    tax := subtotal * 0.06;

         2. SELECT INTO:

    通過該語句可以爲記錄變量或行類型變量進行賦值,其表現形式爲:SELECT INTO target select_expressions FROM ..., 該賦值方式一次只能賦值一個變量。表達式中的 target 可以表示爲是一個記錄變量、行變量,或者是一組用逗號分隔的簡單變量和記 錄/行字段的列表。select_expressions 以及剩餘部分和普通 SQL 一樣。

    如果將一行或者一個變量列表用做目標,那麼選出的數值必需精確匹配目標的結構,否則就會產生運行時錯誤。如果目標是一個記 錄變量,那麼它自動將自己構造成命令結果列的行類型。如果命令返回零行,目標被賦予空值。如果命令返回多行,那麼將只有第一 行被賦予目標,其它行將被忽略。在執行 SELECT INTO 語句之後,可以通過檢查內置變量 FOUND 來判斷本次賦值是否成功,如:
     SELECT INTO myrec * FROM emp WHERE empname = myname;

    IF NOT FOUND THEN

        RAISE EXCEPTION 'employee % not found', myname;

    END IF;

    要測試一個記錄/行結果是否爲空,可以使用 IS NULL 條件進行判斷,但是對於返回多條記錄的情況則無法判斷,如:     DECLARE

        users_rec RECORD; 
    BEGIN

        SELECT INTO users_rec * FROM users WHERE user_id = 3;

        IF users_rec.homepage IS NULL THEN

            RETURN 'http://';

        END IF;

    END;

         3. 執行一個沒有結果的表達式或者命令:

    在調用一個表達式或執行一個命令時,如果對其返回的結果不感興趣,可以考慮使用 PERFORM 語句:PERFORM query,該 語句將執行 PERFORM 之後的命令並忽略其返回的結果。其中 query 的寫法和普通的 SQL SELECT 命令是一樣的,只是把開頭的關 鍵字 SELECT 替換成 PERFORM,如:

    PERFORM create_mv('cs_session_page_requests_mv', my_query); 
 
    4. 執行動態命令:

    如果在 PL/pgSQL 函數中操作的表或數據類型在每次調用該函數時都可能會發生變化,在這樣的情況下,可以考慮使用 PL/pgSQL 提供的 EXECUTE 語句:EXECUTE command-string [ INTO target ],其中 command-string 是用一段文本表示的表達式, 它包含要執行的命令。而 target 是一個記錄變量、行變量或者一組用逗號分隔的簡單變量和記錄/行域的列表。這裏需要特別注意的 是,該命令字符串將不會發生任何 PL/pgSQL 變量代換,變量的數值必需在構造命令字符串時插入到該字符串中。     和所有其它 PL/pgSQL 命令不同的是,一個由 EXECUTE 語句運行的命令在服務器內並不會只 prepare 和保存一次。相反,該語 句在每次運行的時候,命令都會 prepare 一次。因此命令字符串可以在函數裏動態的生成以便於對各種不同的表和字段進行操作,從 而提高函數的靈活性。然而由此換來的卻是性能上的折損。見如下示例:

    EXECUTE 'UPDATE tbl SET ' || quote_ident(columnname) || ' = ' || quote_literal(newvalue);  
 
五、控制結構: 
 
    1. 函數返回:

    1). RETURN expression

    該表達式用於終止當前的函數,然後再將 expression 的值返回給調用者。如果返回簡單類型,那麼可以使用任何表達式,同時表 達式的類型也將被自動轉換成函數的返回類型,就像我們在賦值中描述的那樣。如果要返回一個複合類型的數值,則必須讓表達式返 回記錄或者匹配的行變量。

    2). RETURN NEXT expression

    如果 PL/pgSQL 函數聲明爲返回 SETOF sometype,其行記錄是通過 RETURN NEXT 命令進行填充的,直到執行到不帶參數的 RETURN 時才表示該函數結束。因此對於 RETURN NEXT 而言,它實際上並不從函數中返回,只是簡單地把表達式的值保存起來, 然後繼續執行 PL/pgSQL 函數裏的下一條語句。隨着 RETURN NEXT 命令的迭代執行,結果集最終被建立起來。該類函數的調用方 式如下:

    SELECT * FROM some_func();

    它被放在 FROM 子句中作爲數據源使用。最後需要指出的是,如果結果集數量很大,那麼通過該種方式來構建結果集將會導致極 大的性能損失。 
 
    2. 條件:

    在 PL/pgSQL 中有以下三種形式的條件語句。

    1). IF-THEN

    IF boolean-expression THEN

        statements

    END IF;

        2). IF-THEN-ELSE

    IF boolean-expression THEN

        statements 
    ELSE

        statements

    END IF;  

  3). IF-THEN-ELSIF-ELSE

    IF boolean-expression THEN

        statements

    ELSIF boolean-expression THEN

        statements

    ELSIF boolean-expression THEN

        statements

    ELSE

        statements

    END IF;

        關於條件語句,這裏就不在做過多的贅述了。 
 
    3. 循環:

    1). LOOP

    LOOP

        statements

    END LOOP [ label ];

    LOOP 定義一個無條件的循環,直到由 EXIT 或者 RETURN 語句終止。可選的 label 可以由 EXIT 和 CONTINUE 語句使用,用於 在嵌套循環中聲明應該應用於哪一層循環。

     2). EXIT

    EXIT [ label ] [ WHEN expression ];

    如果沒有給出 label,就退出最內層的循環,然後執行跟在 END LOOP 後面的語句。如果給出 label,它必須是當前或更高層的嵌 套循環塊或語句塊的標籤。之後該命名塊或循環就會終止,而控制則直接轉到對應循環/塊的 END 語句後面的語句上。     如果聲明瞭 WHEN,EXIT 命令只有在 expression 爲真時才被執行,否則將直接執行 EXIT 後面的語句。見如下示例:

    LOOP

        -- do something

        EXIT WHEN count > 0;

    END LOOP;  

  3). CONTINUE

    CONTINUE [ label ] [ WHEN expression ];  

  如果沒有給出 label,CONTINUE 就會跳到最內層循環的開始處,重新進行判斷,以決定是否繼續執行循環內的語句。如果指定 label,則跳到該 label 所在的循環開始處。如果聲明瞭 WHEN,CONTINUE 命令只有在 expression 爲真時才被執行,否則將直接 執行 CONTINUE 後面的語句。見如下示例:

    LOOP

        -- do something

        EXIT WHEN count > 100;

        CONTINUE WHEN count < 50;

    END LOOP;

        4). WHILE

    [ <<label>> ]

     WHILE expression LOOP

        statements

    END LOOP [ label ];

    只要條件表達式爲真,其塊內的語句就會被循環執行。條件是在每次進入循環體時進行判斷的。見如下示例:

    WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP

        --do something
    END LOOP;

    5). FOR

    [ <<label>> ]

    FOR name IN [ REVERSE ] expression .. expression LOOP

        statements

    END LOOP [ label ];

    變量name自動被定義爲integer類型,其作用域僅爲FOR循環的塊內。表示範圍上下界的兩個表達式只在進入循環時計算一次。 每次迭代 name 值自增 1,但如果聲明瞭 REVERSE,name 變量在每次迭代中將自減 1,見如下示例:

    FOR i IN 1..10 LOOP

        --do something

        RAISE NOTICE 'i IS %', i;

    END LOOP;

         FOR i IN REVERSE 10..1 LOOP

        --do something

    END LOOP;  

           4. 遍歷命令結果:

    [ <<label>> ]

    FOR record_or_row IN query LOOP

        statements

    END LOOP [ label ];

    這是另外一種形式的 FOR 循環,在該循環中可以遍歷命令的結果並操作相應的數據,見如下示例:

    FOR rec IN SELECT * FROM some_table LOOP

        PERFORM some_func(rec.one_col);

    END LOOP;

    PL/pgSQL 還提供了另外一種遍歷命令結果的方式,和上面的方式相比,唯一的差別是該方式將 SELECT 語句存於字符串文本中, 然後再交由 EXECUTE 命令動態的執行。和前一種方式相比,該方式的靈活性更高,但是效率較低。

    [ <<label>> ]

    FOR record_or_row IN EXECUTE text_expression LOOP

        statements

    END LOOP [ label ];

         5. 異常捕獲:     在 PL/pgSQL 函數中,如果沒有異常捕獲,函數會在發生錯誤時直接退出,與其相關的事物也會隨之回滾。我們可以通過使用帶 有 EXCEPTION 子句的 BEGIN 塊來捕獲異常並使其從中恢復。見如下聲明形式:

    [ <<label>> ]

    [ DECLARE

        declarations ]

    BEGIN

        statements

    EXCEPTION

        WHEN condition [ OR condition ... ] THEN  

          handler_statements

        WHEN condition [ OR condition ... ] THEN

            handler_statements

    END;

    如果沒有錯誤發生,只有 BEGIN 塊中的 statements 會被正常執行,然而一旦這些語句中有任意一條發生錯誤,其後的語句都將 被跳過,直接跳轉到 EXCEPTION 塊的開始處。此時系統將搜索異常條件列表,尋找匹配該異常的第一個條件,如果找到匹配,則執 行相應的 handler_statements,之後再執行 END 的下一條語句。如果沒有找到匹配,該錯誤就會被繼續向外拋出,其結果與沒有 EXCEPTION 子句完全等同。如果此時 handler_statements 中的語句發生新錯誤,它將不能被該 EXCEPTION 子句捕獲,而是繼 續向外傳播,交由其外層的 EXCEPTION 子句捕獲並處理。見如下示例:

    INSERT INTO mytab(firstname, lastname) VALUES('Tom', 'Jones');

    BEGIN

        UPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones';

        x := x + 1;

        y := x / 0;

    EXCEPTION

        WHEN division_by_zero THEN

            RAISE NOTICE 'caught division_by_zero';

            RETURN x;

    END;

    當以上函數執行到 y := x / 0 語句時,將會引發一個異常錯誤,代碼將跳轉到 EXCEPTION 塊的開始處,之後系統會尋找匹配的 異常捕捉條件,此時 division_by_zero 完全匹配,這樣該條件內的代碼將會被繼續執行。需要說明的是,RETURN 語句中返回的 x 值爲 x := x + 1 執行後的新值,但是在除零之前的 update 語句將會被回滾,BEGIN 之前的 insert 語句將仍然生效。     
六、遊標: 
 
    1. 聲明遊標變量:

    在 PL/pgSQL 中對遊標的訪問都是通過遊標變量實現的,其數據類型爲 refcursor。 創建遊標變量的方法有以下兩種:

    1). 和聲明其他類型的變量一樣,直接聲明一個遊標類型的變量即可。

    2). 使用遊標專有的聲明語法,如:

    name CURSOR [ ( arguments ) ] FOR query;

    其中 arguments 爲一組逗號分隔的 name datatype 列表,見如下示例:

    curs1 refcursor;

    curs2 CURSOR FOR SELECT * FROM tenk1;

    curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;

    在上面三個例子中,只有第一個是未綁定遊標,剩下兩個遊標均已被綁定。 
 
    2. 打開遊標:

    遊標在使用之前必須先被打開,在 PL/pgSQL 中有三種形式的 OPEN 語句,其中兩種用於未綁定的遊標變量,另外一種用於綁定 的遊標變量。

    1). OPEN FOR:

     其聲明形式爲:

    OPEN unbound_cursor FOR query;

    該形式只能用於未綁定的遊標變量,其查詢語句必須是 SELECT,或其他返回記錄行的語句,如 EXPLAIN。在 PostgreSQL 中, 該查詢和普通的 SQL 命令平等對待,即先替換變量名,同時也將該查詢的執行計劃緩存起來,以供後用。見如下示例:     OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;     2). OPEN FOR EXECUTE     其聲明形式爲:

    OPEN unbound_cursor FOR EXECUTE query-string;

       和上面的形式一樣,該形式也僅適用於未綁定的遊標變量。EXECUTE 將動態執行其後以文本形式表示的查詢字符串。     OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);

    3). 打開一個綁定的遊標     其聲明形式爲:

    OPEN bound_cursor [ ( argument_values ) ];  

     該形式僅適用於綁定的遊標變量,只有當該變量在聲明時包含接收參數,才能以傳遞參數的形式打開該遊標,這些參數將被實際代 入到遊標聲明的查詢語句中,見如下示例:

    OPEN curs2;

    OPEN curs3(42);     
 
    3. 使用遊標:

    遊標一旦打開,就可以按照以下方式進行讀取。然而需要說明的是,遊標的打開和讀取必須在同一個事物內,因爲在 PostgreSQL 中,如果事物結束,事物內打開的遊標將會被隱含的關閉。

    1). FETCH     其聲明形式爲:

    FETCH cursor INTO target;

    FETCH 命令從遊標中讀取下一行記錄的數據到目標中,其中目標可以是行變量、記錄變量,或者是一組逗號分隔的普通變量的列 表,讀取成功與否,可通過 PL/pgSQL 內置變量 FOUND 來判斷,其規則等同於 SELECT INTO。見如下示例:

    FETCH curs1 INTO rowvar;  --rowvar 爲行變量
     FETCH curs2 INTO foo, bar, baz;  

  2). CLOSE

    其聲明形式爲:

    CLOSE cursor;

    關閉當前已經打開的遊標,以釋放其佔有的系統資源,見如下示例:

    CLOSE curs1; 
 
七、錯誤和消息: 
 
    在 PostgreSQL 中可以利用 RAISE 語句報告信息和拋出錯誤,其聲明形式爲:

    RAISE level 'format' [, expression [, ...]];

    這裏包含的級別有 DEBUG(向服務器日誌寫信息)、LOG(向服務器日誌寫信息,優先級更高)、INFO、NOTICE 和 WARNING(把 信息寫到服務器日誌以及轉發到客戶端應用,優先級逐步升高)和 EXCEPTION 拋出一個錯誤(通常退出當前事務)。某個優先級別的 信息是報告給客戶端還是寫到服務器日誌,還是兩個均有,是由 log_min_messages 和 client_min_messages 這兩個系統初始化 參數控制的。

    在 format 部分中,%表示爲佔位符,其實際值僅在 RAISE 命令執行時由後面的變量替換,如果要在 format 中表示%自身,可 以使用%%的形式表示,見如下示例:

    RAISE NOTICE 'Calling cs_create_job(%)',v_job_id;  --v_job_id 變量的值將替換 format 中的 % 。
     RAISE EXCEPTION 'Inexistent ID --> %',user_id;    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章