<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 40.6\. 控制結構 控制結構可能是PL/pgSQL中最有用的(以及最重要)的部分了。 利用PL/pgSQL的控制結構, 你可以以非常靈活而且強大的方法操縱PostgreSQL的數據。 ## 40.6.1\. 從函數返回 有兩個命令可以用來從函數中返回數據:`RETURN`和 `RETURN NEXT`。 ### 40.6.1.1\. `RETURN` ``` RETURN _expression_; ``` 帶表達式的`RETURN`用于終止函數 并把`_expression_`的值返回給調用者。 這種形式用于不返回集合的PL/pgSQL函數。 如果函數中返回標量類型,那么表達式結果將被自動轉換成函數的返回類型, 就像在賦值中描述的那樣。但是要返回一個復合(行)數值, 你必須寫一個準確提供需求列集合的表達式,這可能需要顯式轉換。 如果你聲明帶有輸出參數的函數,那么就只需要寫無表達式的`RETURN`。 那么輸出參數變量的當前值將被返回。 如果你聲明函數返回`void`,那么一個`RETURN` 語句可以用于提前退出函數; 但是不要在`RETURN`后面寫一個表達式。 一個函數的返回值不能是未定義。 如果控制到達了函數最頂層的塊而沒有碰到一個`RETURN`語句, 那么它就會發生一個錯誤。不過,這個限制不適用于帶輸出參數的函數以及那些返回`void`的函數。 在這些例子里,如果頂層的塊結束,則自動執行一個`RETURN`語句。 例子: ``` -- 返回一個標量類型函數 RETURN 1 + 2; RETURN scalar_var; -- 返回復合類型函數 RETURN composite_type_var; RETURN (1, 2, 'three'::text); -- must cast columns to correct types ``` ### 40.6.1.2\. `RETURN NEXT`和`RETURN QUERY` ``` RETURN NEXT _expression_; RETURN QUERY _query_; RETURN QUERY EXECUTE _command-string_ [ USING `_expression_` [, ... ] ]; ``` 如果一個PL/pgSQL函數聲明為返回`SETOF` `_sometype_`, 那么遵循的過程則略有不同。 在這種情況下,要返回的獨立項是在`RETURN NEXT`或者 `RETURN QUERY`命令里聲明的, 然后最后有一個不帶參數的`RETURN`命令用于告訴這個函數已經完成執行了。 `RETURN NEXT`可以用于標量和復合數據類型;對于復合類型, 將返回一個完整的結果"table"。 `RETURN QUERY`命令將一條查詢的結果追加到一個函數的結果集中。 `RETURN NEXT`和`RETURN QUERY`在單一集合返回 函數中自由混合,在這種情況下,結果將被級聯。 `RETURN NEXT`和`RETURN QUERY`實際上不會從函數中返回, 它們是將零或者多個行追加到函數的結果集中。 然后繼續執行PL/pgSQL函數里的下一條語句。 隨著后繼的`RETURN NEXT`或者`RETURN QUERY`命令的執行, 結果集就建立起來了。最后一個`RETURN`應該沒有參數, 它導致控制退出該函數(或者你可以簡單地讓控制到達函數的結尾)。 `RETURN QUERY`有一個變形`RETURN QUERY EXECUTE`, 指定查詢將被動態執行。 參數表達式可以通過`USING`插入到計算查詢字符串中,以`EXECUTE`命令的同樣方式。 如果你聲明函數帶有輸出參數,那么就只需要寫不帶表達式的`RETURN NEXT`。 輸出參數的當前值將被保存,用于最終返回。請注意如果有多個輸出參數, 比如聲明函數為返回`SETOF record`或者是在只有一個類 型為`_sometype_`的輸出參數時聲明 為`SETOF` `_sometype_`, 這樣才能創建一個帶有輸出參數的返回集合的函數。 下面是一個使用`RETURN NEXT`的函數例子: ``` CREATE TABLE foo (fooid INT, foosubid INT, fooname TEXT); INSERT INTO foo VALUES (1, 2, 'three'); INSERT INTO foo VALUES (4, 5, 'six'); CREATE OR REPLACE FUNCTION get_all_foo() RETURNS SETOF foo AS $BODY$ DECLARE r foo%rowtype; BEGIN FOR r IN SELECT * FROM foo WHERE fooid > 0 LOOP -- 可以在這里做一些處理 RETURN NEXT r; -- return current row of SELECT END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; SELECT * FROM get_all_foo(); ``` 這是一個使用`RETURN QUERY`的函數例子: ``` CREATE FUNCTION get_available_flightid(date) RETURNS SETOF integer AS $BODY$ BEGIN RETURN QUERY SELECT flightid FROM flight WHERE flightdate >= $1 AND flightdate < ($1 + 1); -- 由于沒有完成執行,我們可以檢查行是否返回并且如果沒有則拋出異常。 IF NOT FOUND THEN RAISE EXCEPTION 'No flight at %.', $1; END IF; RETURN; END $BODY$ LANGUAGE plpgsql; -- 如果沒有可用航班,則返回可用航班或者拋出異常。 SELECT * FROM get_available_flightid(CURRENT_DATE); ``` > **Note:** 目前`RETURN NEXT`和`RETURN QUERY` 實現在從函數返回之前把整個結果集都保存起來, 就像上面描述的那樣。這意味著如果一個PL/pgSQL函數生成一個非常大的結果集, 性能可能會很差:數據將被寫到磁盤上以避免內存耗盡, 但是函數在完成整個結果集的生成之前不會退出。 將來的PL/pgSQL版本可能會允許用戶定義沒有這樣限制的返回集合的函數。 目前,數據開始向磁盤里寫的時刻是由配置變量[work_mem](#calibre_link-1374)控制的。 擁有足夠內存的管理員如果想在內存里存儲更大的結果集, 則可以考慮把這個參數增大一些。 ## 40.6.2\. 條件 `IF`和`CASE`語句讓你可以根據某種條件執行命令。 PL/pgSQL有三種形式的`IF`: * `IF ... THEN` * `IF ... THEN ... ELSE` * `IF ... THEN ... ELSIF ... THEN ... ELSE` 以及兩種形式的`CASE`: * `CASE ... WHEN ... THEN ... ELSE ... END CASE` * `CASE WHEN ... THEN ... ELSE ... END CASE` ### 40.6.2.1\. `IF-THEN` ``` IF _boolean-expression_ THEN _statements_ END IF; ``` `IF-THEN`語句是`IF`的最簡單形式。如果條件為真, 在`THEN`和`END IF`之間的語句將被執行。 否則,將忽略它們。 例如: ``` IF v_user_id <> 0 THEN UPDATE users SET email = v_email WHERE user_id = v_user_id; END IF; ``` ### 40.6.2.2\. `IF-THEN-ELSE` ``` IF _boolean-expression_ THEN _statements_ ELSE _statements_ END IF; ``` `IF-THEN-ELSE`語句增加了`IF-THEN`的分支, 讓你可以聲明在條件為假的時候執行的語句。(請注意這包含條件是NULL的情況)。 例如: ``` IF parentid IS NULL OR parentid = '' THEN RETURN fullname; ELSE RETURN hp_true_filename(parentid) || '/' || fullname; END IF; ``` ``` IF v_count > 0 THEN INSERT INTO users_count (count) VALUES (v_count); RETURN 't'; ELSE RETURN 'f'; END IF; ``` ### 40.6.2.3\. `IF-THEN-ELSIF` ``` IF _boolean-expression_ THEN _statements_ [ ELSIF `_boolean-expression_` THEN `_statements_` [ ELSIF `_boolean-expression_` THEN `_statements_` ...]] [ ELSE `_statements_` ] END IF; ``` 有時不止兩個選擇。`IF-THEN-ELSIF` 反過來提供了一個簡便的方法來檢查選擇條件。 `IF`判斷會陸續檢查,直到找到第一個為真的,然后執行相關聲明,如此, 直到`END IF`(_不會_檢測`IF`子查詢)。 如果沒有一個條件符合`IF`判斷,那么會接著執行`ELSE`判斷。 例如: ``` IF number = 0 THEN result := 'zero'; ELSIF number > 0 THEN result := 'positive'; ELSIF number < 0 THEN result := 'negative'; ELSE -- 唯一可能性是號碼為空 result := 'NULL'; END IF; ``` `ELSIF`關鍵字也可以寫成`ELSEIF`。 另一個可以實現該目的的方法是使用`IF-THEN-ELSE`聲明,如下: ``` IF demo_row.sex = 'm' THEN pretty_sex := 'man'; ELSE IF demo_row.sex = 'f' THEN pretty_sex := 'woman'; END IF; END IF; ``` 然而,這個方法需要為每個`IF`寫`END IF`, 因此當有很多選擇時,這種方法明顯比`ELSIF`繁瑣。 ### 40.6.2.4\. 簡單`CASE` ``` CASE _search-expression_ WHEN _expression_ [, `_expression_` [ ... ]] THEN _statements_ [ WHEN `_expression_` [, `_expression_` [ ... ]] THEN `_statements_` ... ] [ ELSE `_statements_` ] END CASE; ``` `CASE`簡單的形式提供基于操作數平等的條件執行。`_search-expression_`被評價并且 先后比較`WHEN`子句中的每個`_表達式_`。 如果找到匹配,那么相應的`_statements_`被執行, 然后控制在`END CASE`之后傳遞到下一個語句。 (隨后的`WHEN`表達式不被評估。) 如果沒有發現匹配,執行`ELSE` `_statements_`; 但如果`ELSE`是不存在的,然后引發`CASE_NOT_FOUND`異常。 例如: ``` CASE x WHEN 1, 2 THEN msg := 'one or two'; ELSE msg := 'other value than one or two'; END CASE; ``` ### 40.6.2.5\. 搜索`CASE` ``` CASE WHEN _boolean-expression_ THEN _statements_ [ WHEN `_boolean-expression_` THEN `_statements_` ... ] [ ELSE `_statements_` ] END CASE; ``` `CASE`搜索形式基于布爾表達式的真理提供條件執行。 每個`WHEN`子句的`_boolean-expression_`依次被評估, 直到找到一個產生`true`為止。 然后執行相應的`_statements_`, 控制`END CASE`之后傳遞到下一個語句。 (隨后不評估`WHEN`表達式)。 如果發現沒有真實結果,則執行`ELSE``_statements_`; 但如果`ELSE`是不存在的,那么引發 `CASE_NOT_FOUND`異常。 例如: ``` CASE WHEN x BETWEEN 0 AND 10 THEN msg := 'value is between zero and ten'; WHEN x BETWEEN 11 AND 20 THEN msg := 'value is between eleven and twenty'; END CASE; ``` `CASE`這種形式完全等價于 `IF-THEN-ELSIF`,除了達到忽略錯誤中的`ELSE`子句結果而不是什么都不做的規則。 ## 40.6.3\. 簡單循環 使用`LOOP`, `EXIT`,`CONTINUE`, `WHILE`, `FOR`和`FOREACH`語句, 可以控制PL/pgSQL函數重復一系列命令。 ### 40.6.3.1\. `循環` ``` [ <<`_label_`>> ] LOOP _statements_ END LOOP [ `_label_` ]; ``` `LOOP`定義一個無條件的循環,無限循環, 直到由`EXIT`或者 `RETURN`語句終止。 可選的`_label_`可以由`EXIT` 和`CONTINUE`語句使用, 用于在嵌套循環中聲明應該應用于哪一層循環。 ### 40.6.3.2\. `退出` ``` EXIT [ `_label_` ] [ WHEN `_boolean-expression_` ]; ``` 如果沒有給出`_label_`,那么退出最內層的循環, 然后執行跟在`END LOOP`后面的語句。 如果給出`_label_`, 那么它必須是當前或者更高層的嵌套循環塊或者語句塊的標簽。 然后該命名塊或者循環就會終止,而控制落到對應循環/塊的`END`語句后面的語句上。 如果聲明了`WHEN`, 循環退出只有在`_boolean-expression_`為真的時候才發生, 否則控制會落到`EXIT`后面的語句上。 `EXIT`可以用于在所有的循環類型中,它并不僅僅限制于在無條件循環中使用。 在和`BEGIN`塊一起使用的時候,`EXIT`把控制交給塊結束后的下一個語句。 需要注意的是,一個標簽必須用于這個目的; 一個沒有標記的`EXIT`永遠無法與`BEGIN`進行匹配。 (這是PostgreSQL 8.4之前版本的一個變化, 這將允許未標記`EXIT`匹配`BEGIN`塊)。 例如: ``` LOOP -- 一些計算 IF count > 0 THEN EXIT; -- exit loop END IF; END LOOP; LOOP -- 一些計算 EXIT WHEN count > 0; -- 和前面的例子相同結果 END LOOP; <<ablock>> BEGIN -- 一些計算 IF stocks > 100000 THEN EXIT ablock; -- 導致從BEGIN塊退出 END IF; -- 忽略這兒的計算,當stocks > 100000時 END; ``` ### 40.6.3.3\. `CONTINUE` ``` CONTINUE [ `_label_` ] [ WHEN `_boolean-expression_` ]; ``` 如果沒有給出`_label_`,那么就開始最內層循環的下一次執行。 也就是說,控制傳遞回給循環控制表達式(如果有),然后重新計算循環體。 如果出現了`_label_`,它聲明即將繼續執行的循環的標簽。 如果聲明了`WHEN`,那么循環的下一次執行只有 在`_boolean-expression_`為真的情況下才進行。 否則,控制傳遞給`CONTINUE`后面的語句。 `CONTINUE`可以用于所有類型的循環;它并不僅僅限于無條件循環。 例如: ``` LOOP -- 一些計算 EXIT WHEN count > 100; CONTINUE WHEN count < 50; -- 在[50 .. 100]內的計算 END LOOP; ``` ### 40.6.3.4\. `WHILE` ``` [ <<`_label_`>> ] WHILE _boolean-expression_ LOOP _statements_ END LOOP [ `_label_` ]; ``` 只要條件表達式(`_boolean-expression_`)為真, `WHILE`語句就會不停的在一系列語句上進行循環, 條件是在每次進入循環體的時候被檢查。 例如: ``` WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP -- 這里的一些計算 END LOOP; WHILE NOT done LOOP -- 這里的一些計算 END LOOP; ``` ### 40.6.3.5\. `FOR` (Integer 變量) ``` [ <<`_label_`>> ] FOR _name_ IN [ REVERSE ] _expression_ .. _expression_ [ BY `_expression_` ] LOOP _statements_ END LOOP [ `_label_` ]; ``` 這種形式的`FOR`對一定范圍的整數進行迭代的循環。 變量`_name_`會自動定義為`BY`類型并且只在循環里存在 (任何該變量名的現存定義在此循環內都將被忽略)。 給出范圍上下界的兩個表達式在進入循環的時候計算一次。 `BY`子句指定迭代步長(缺省為 1), 但如果聲明了`REVERSE`步長將變為相應的負值。 一些整數`FOR`循環的例子: ``` FOR i IN 1..10 LOOP -- 我將在值1,2,3,4,5,6,7,8,9,10中循環 END LOOP; FOR i IN REVERSE 10..1 LOOP -- 將在值10,9,8,7,6,5,4,3,2,1中循環 END LOOP; FOR i IN REVERSE 10..1 BY 2 LOOP -- 將在值10,8,6,4,2中循環 END LOOP; ``` 如果下界大于上界(或者是在`REVERSE`情況下是小于), 那么循環體將完全不被執行。 而且不會拋出任何錯誤。 如果`_label_`被附加到`FOR`循環,那么整數循環變量 可以使用`_label_`引用適當名稱。 ## 40.6.4\. 遍歷命令結果 使用不同類型的`FOR`循環, 你可以遍歷一個命令的結果并且對其進行相應的操作。語法是: ``` [ <<`_label_`>> ] FOR _target_ IN _query_ LOOP _statements_ END LOOP [ `_label_` ]; ``` `_target_`是一個記錄變量、 行變量、逗號分隔的標量變量列表`_target_` 被連續不斷賦予所有來自`_query_`的行, 并且循環體將為每行執行一次。 下面是一個例子: ``` CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$ DECLARE mviews RECORD; BEGIN RAISE NOTICE 'Refreshing materialized views...'; FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP -- 現在"mviews"里有了一條來自 cs_materialized_views 的記錄 RAISE NOTICE 'Refreshing materialized view %s ...', quote_ident(mviews.mv_name); EXECUTE 'TRUNCATE TABLE ' || quote_ident(mviews.mv_name); EXECUTE 'INSERT INTO ' || quote_ident(mviews.mv_name) || ' ' || mviews.mv_query; END LOOP; RAISE NOTICE 'Done refreshing materialized views.'; RETURN 1; END; $$ LANGUAGE plpgsql; ``` 如果循環是用一個`EXIT`語句終止的, 那么在循環之后你仍然可以訪問最后賦值的行。 `FOR`語句中使用的這種`_query_`可以是任何返回行的SQL命令, 通常是`SELECT`,不過帶有`RETURNING`子句的`INSERT`, `UPDATE` 或`DELETE`也是可以的, 一些諸如`EXPLAIN`之類的命令也可以。 PL/pgSQL變量代替查詢文本,并且查詢計劃為了重新使用被緩存,正如 [Section 40.10.1](#calibre_link-1045)和[Section 40.10.2](#calibre_link-1573)。 `FOR-IN-EXECUTE`語句是遍歷所有行的另外一種方法: ``` [ <<`_label_`>> ] FOR _target_ IN EXECUTE _text_expression_ [ USING `_expression_` [, ... ] ] LOOP _statements_ END LOOP [ `_label_` ]; ``` 這個例子類似前面的形式,只不過源查詢語句聲明為了一個字符串表達式, 這樣它在每次進入`FOR`循環的時候都會重新計算和生成執行計劃。 這樣就允許程序員在一個預先規劃好了的命令所獲得的速度和一個動態命令所獲得的靈活性 (就像一個簡單的`EXECUTE`語句那樣)之間進行選擇。 當使用`EXECUTE`時, 可以通過`USING`將參數值插入到動態命令中。 對于一個需要將結果迭代的查詢, 另外一個聲明的方法是將它定義為游標(cursor), 可參閱[Section 40.7.4](#calibre_link-1580)。 ## 40.6.5\. 遍歷數組 `FOREACH`循環類似于`FOR`循環, 但不是遍歷SQL查詢返回的行,它遍歷數組值元素。 (一般而言,`FOREACH`是遍歷復合值表達式組成部分; 循環遍歷除數組外的復合值變量將來可以被添加。) `FOREACH`語句循環數組是: ``` [ <<`_label_`>> ] FOREACH _target_ [ SLICE `_number_` ] IN ARRAY _expression_ LOOP _statements_ END LOOP [ `_label_` ]; ``` 沒有`SLICE`,或者如果聲明`SLICE 0`,則 循環遍歷通過評估`_expression_`產生的數組的單個元素。 `_target_`變量分配每個 序列中的元素值,并為每個元素執行循環體。 這里是遍歷整數數組元素的一個例子: ``` CREATE FUNCTION sum(int[]) RETURNS int8 AS $$ DECLARE s int8 := 0; x int; BEGIN FOREACH x IN ARRAY $1 LOOP s := s + x; END LOOP; RETURN s; END; $$ LANGUAGE plpgsql; ``` 元素以存儲順序進行訪問,不論數組維數的數量。盡管`_target_` 通常只是一個單一的變量,當循環復合值的數組(記錄)時,它可以是一個變量列表, 在這種情況下,每個數組元素,從連續的復合值列中分配變量。 以正數`SLICE`值,`FOREACH`遍歷數組的元素部分,而不是單一元素。 `SLICE`的值必須是不大于數組維數的整數常數。 `_target_`變量必須是一個數組, 并且它接收數組值的連續片段,而每個片段 是通過`SLICE`指定的維數。這里是遍歷一維切片的一個例子: ``` CREATE FUNCTION scan_rows(int[]) RETURNS void AS $$ DECLARE x int[]; BEGIN FOREACH x SLICE 1 IN ARRAY $1 LOOP RAISE NOTICE 'row = %', x; END LOOP; END; $$ LANGUAGE plpgsql; SELECT scan_rows(ARRAY[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]); NOTICE: row = {1,2,3} NOTICE: row = {4,5,6} NOTICE: row = {7,8,9} NOTICE: row = {10,11,12} ``` ## 40.6.6\. 捕獲錯誤 缺省時,一個在PL/pgSQL函數里發生的錯誤退出函數的執行, 并且實際上其周圍的事務也會退出。 你可以使用一個帶有`EXCEPTION`子句的`BEGIN`塊捕獲錯誤并且從中恢復。 其語法是正常的`BEGIN`塊語法的一個擴展: ``` [ <<`_label_`>> ] [ DECLARE `_declarations_` ] BEGIN _statements_ EXCEPTION WHEN _condition_ [ OR `_condition_` ... ] THEN _handler_statements_ [ WHEN `_condition_` [ OR `_condition_` ... ] THEN `_handler_statements_` ... ] END; ``` 如果沒有發生錯誤,這種形式的塊只是簡單地執行所有`_statements_`, 然后轉到下一個`END`之后的語句。 但是如果在`_statements_`內部發生了一個錯誤, 則對`_statements_`的進一步處理將廢棄,然后轉到`EXCEPTION`列表。 系統搜索這個列表,尋找匹配錯誤的第一個`_condition_`。 如果找到匹配,則執行對應的`_handler_statements_`, 然后轉到`END`之后的下一個語句。如果沒有找到匹配,該錯誤就會廣播出去, 就好像根本沒有`EXCEPTION`子句一樣: 該錯誤可以被一個包圍塊用`EXCEPTION`捕獲, 如果沒有包圍塊,則退出函數的處理。 `_condition_`的名字可以是[Appendix A](#calibre_link-120)里顯示的任何名字。 一個范疇名匹配任意該范疇里的錯誤。 特殊的條件名`OTHERS`匹配除了`QUERY_CANCELED`之外的所有錯誤類型。 可以用名字捕獲`QUERY_CANCELED`,不過通常是不明智的。 條件名是大小寫無關的。同時也可以通過`SQLSTATE`來聲明一個錯誤條件, 例如: ``` WHEN division_by_zero THEN ... WHEN SQLSTATE '22012' THEN ... ``` 如果在選中的`_handler_statements_`里發生了新錯誤, 那么它不能被這個`EXCEPTION`子句捕獲,而是傳播出去。 一個外層的`EXCEPTION`子句可以捕獲它。 如果一個錯誤被`EXCEPTION`捕獲,PL/pgSQL函數的局部變量保持錯誤發生時的原值, 但是所有該塊中想固化在數據庫中的狀態都回滾。 作為一個例子,讓我們看看下面片斷: ``` 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`賦值的地方時, 它會帶著一個`division_by_zero`錯誤失敗。 這個錯誤將被`EXCEPTION`子句捕獲。 而在`RETURN`語句里返回的數值將是`x`的增量值。 但是`UPDATE`已經被回滾。然而,在該塊之前的`INSERT`將不會回滾, 因此最終的結果是數據庫包含`Tom Jones`而不是`Joe Jones`。 > **Tip:** 進入和退出一個包含`EXCEPTION`子句的塊要比不包含的塊開銷大的多。 因此,不必要的時候不要使用`EXCEPTION`。 **Example 40-2\. `UPDATE`/`INSERT`異常** 這個例子根據使用異常處理器執行恰當的`UPDATE`或者`INSERT`。 ``` CREATE TABLE db (a INT PRIMARY KEY, b TEXT); CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS $$ BEGIN LOOP -- 第一次嘗試更新key UPDATE db SET b = data WHERE a = key; IF found THEN RETURN; END IF; -- 不存在,所以嘗試插入key,如果其他人同時插入相同的key,我們可能得到唯一key失敗。 BEGIN INSERT INTO db(a,b) VALUES (key, data); RETURN; EXCEPTION WHEN unique_violation THEN -- 什么也不做,并且循環嘗試再次更新。 END; END LOOP; END; $$ LANGUAGE plpgsql; SELECT merge_db(1, 'david'); SELECT merge_db(1, 'dennis'); ``` 這個代碼假設通過`INSERT`不是說表上觸發器函數中的`INSERT` 產生`unique_violation`錯誤,如果表上有超過一個以上的唯一索引,它可能行為不端, 因為將重試操作不論哪個索引產生錯誤。 可以通過特性討論下一步檢查捕獲的錯誤是預期的來獲取更高的安全性。 ### 40.6.6.1\. 獲得有關錯誤的信息 異常處理程序經常需要確定發生的具體錯誤。有兩種方法來獲得 當前PL/pgSQL異常: 特殊變量和`GET STACKED DIAGNOSTICS`命令 的有關信息。 在一個異常處理程序中,特殊變量 `SQLSTATE`包含相當于發生異常的錯誤代碼 (參考[Table A-1](#calibre_link-1092)獲得可能錯誤代碼列)。 特殊變量`SQLERRM`包含與異常有關的錯誤消息。 這些變量是在異常處理外未被定義的。 在一個異常處理程序中,也可以檢索關于使用`GET STACKED DIAGNOSTICS`命令的當前異常信息,形成了: ``` GET STACKED DIAGNOSTICS _variable_ = _item_ [ , ... ]; ``` 每個`_item_`是識別被分配到指定變量的狀態值(應該是接收它的正確數據類型)的一個關鍵字。 目前可用的狀態顯示在[Table 40-1](#calibre_link-1581)中。 **Table 40-1\. 錯誤診斷值** | 名字 | 類型 | 描述 | | --- | --- | --- | | `RETURNED_SQLSTATE` | text | 異常的SQLSTATE錯誤代碼 | | `COLUMN_NAME` | text | 與異常相關的列名 | | `CONSTRAINT_NAME` | text | 與異常相關的約束名 | | `PG_DATATYPE_NAME` | text | 與異常相關的數據類型名 | | `MESSAGE_TEXT` | text | 異常的主要消息文本 | | `TABLE_NAME` | text | 與異常相關的表名 | | `SCHEMA_NAME` | text | 與異常相關的模式名 | | `PG_EXCEPTION_DETAIL` | text | 異常的詳細信息文本,如果任何 | | `PG_EXCEPTION_HINT` | text | 異常的提示信息文本,如果任何 | | `PG_EXCEPTION_CONTEXT` | text | 描述調用堆棧的文本線程 | 如果異常沒有設置項值,則返回空字符串。 例子: ``` DECLARE text_var1 text; text_var2 text; text_var3 text; BEGIN -- 一些處理可能引起異常 ... EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT, text_var2 = PG_EXCEPTION_DETAIL, text_var3 = PG_EXCEPTION_HINT; END; ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看