摘要:在當前GaussDB(DWS)的能力中主要支持兩種過程化SQL語言,即基於PostgreSQL的PL/pgSQL以及基於Oracle的PL/SQL。本篇文章我們通過匿名塊,函數,存儲過程向大家介紹一下GaussDB(DWS)對於過程化SQL語言的基本能力。
本文分享自華為雲社區《GaussDB(DWS) SQL進階之PLSQL(一)-匿名塊、函數和存儲過程》,原文作者:xxxsql123 。
前言
GaussDB(DWS)中的PLSQL語言,是一種可載入的過程語言,其創建的函數可以被用在任何可以使用內建函數的地方。例如,可以創建復雜條件的計算函數並且后面用它們來定義操作符或把它們用於索引表達式。
SQL被大多數數據庫用作查詢語言。它是可移植的並且容易學習。但是每一個SQL語句必須由數據庫服務器單獨執行。
這意味着客戶端應用必須發送每一個查詢到數據庫服務器、等待它被處理、接收並處理結果、做一些計算,然后發送更多查詢給服務器。如果客戶端和數據庫服務器不在同一台機器上,所有這些會引起進程間通信並且將帶來網絡負擔。
通過PLSQL語言,可以將一整塊計算和一系列查詢分組在數據庫服務器內部,這樣就有了一種過程語言的能力並且使SQL更易用,同時能節省的客戶端/服務器通信開銷。
- 客戶端和服務器之間的額外往返通信被消除。
- 客戶端不需要的中間結果不必被整理或者在服務器和客戶端之間傳送。
- 多輪的查詢解析可以被避免。
在當前GaussDB(DWS)的能力中主要支持兩種過程化SQL語言,即基於PostgreSQL的PL/pgSQL以及基於Oracle的PL/SQL。本篇文章我們通過匿名塊,函數,存儲過程向大家介紹一下GaussDB(DWS)對於過程化SQL語言的基本能力。
匿名塊的使用
匿名塊(Anonymous Block)一般用於不頻繁執行的腳本或不重復進行的活動。它們在一個會話中執行,並不被存儲。
在GaussDB(DWS)中通過針對PostgreSQL和Oracle風格的整合,目前支持以下兩種方式調用,對於Oracle遷移到GaussDB(DWS)的存儲過程有了很好的兼容性支持。
√ Oracle風格-以反斜杠結尾:
語法格式:
[DECLARE [declare_statements]]
BEGIN
execution_statements
END;
/
執行用例:
postgres=# DECLARE postgres-# my_var VARCHAR2(30); postgres-# BEGIN postgres$# my_var :='world'; postgres$# dbms_output.put_line('hello '||my_var); postgres$# END; postgres$# / hello world ANONYMOUS BLOCK EXECUTE
√ PostgreSQL風格-以DO開頭,匿名塊用包起來:
語法格式:
DO [ LANGUAGE lang_name ] code;
執行用例:
postgres=# DO $$DECLARE postgres$# my_var char(30); postgres$# BEGIN postgres$# my_var :='world'; postgres$# raise info 'hello %' , my_var; postgres$# END$$; INFO: hello world ANONYMOUS BLOCK EXECUTE
這時細心的小伙伴們就會發現,GaussDB(DWS)不僅支持了Oracle的PL/SQL的兼容性支持,對於Oracle高級包中的dbms_output.put_line函數也做了支持。所以我們也可以將兩個風格混用,發現也是支持的。(^-^)V
postgres=# DO $$DECLARE postgres$# my_var VARCHAR2(30); postgres$# BEGIN postgres$# my_var :='world'; postgres$# dbms_output.put_line('hello '||my_var); postgres$# END$$; hello world ANONYMOUS BLOCK EXECUTE
函數的創建
既然匿名塊GaussDB支持了Oracle和PostgreSQL兩種風格的創建,函數當然也會支持兩種啦。
下面我們一起來看看具體的使用吧!(。ì _ í。)
√ PostgreSQL風格:
語法格式:
CREATE [ OR REPLACE ] FUNCTION function_name ( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ]} [, ...] ] ) [ RETURNS rettype [ DETERMINISTIC ] | RETURNS TABLE ( { column_name column_type } [, ...] )] LANGUAGE lang_name [ {IMMUTABLE | STABLE | VOLATILE } | {SHIPPABLE | NOT SHIPPABLE} | WINDOW | [ NOT ] LEAKPROOF | {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } | {[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | AUTHID DEFINER | AUTHID CURRENT_USER} | {fenced | not fenced} | {PACKAGE} | COST execution_cost | ROWS result_rows | SET configuration_parameter { {TO | =} value | FROM CURRENT }} ][...] { AS 'definition' | AS 'obj_file', 'link_symbol' }
執行用例:
定義函數為SQL查詢的形式:
postgres=# CREATE FUNCTION func_add_sql(integer, integer) RETURNS integer postgres-# AS 'select $1 + $2;' postgres-# LANGUAGE SQL postgres-# IMMUTABLE postgres-# RETURNS NULL ON NULL INPUT; CREATE FUNCTION postgres=# select func_add_sql(1, 2); func_add_sql -------------- 3 (1 row)
定義函數為plpgsql語言的形式:
postgres=# CREATE OR REPLACE FUNCTION func_add_sql2(a integer, b integer) RETURNS integer AS $$ postgres$# BEGIN postgres$# RETURN a + b; postgres$# END; postgres$# $$ LANGUAGE plpgsql; CREATE FUNCTION postgres=# select func_add_sql2(1, 2); func_add_sql2 --------------- 3 (1 row)
定義返回為SETOF RECORD的函數:
postgres=# CREATE OR REPLACE FUNCTION func_add_sql3(a integer, b integer, out sum bigint, out product bigint) postgres-# returns SETOF RECORD postgres-# as $$ postgres$# begin postgres$# sum = a + b; postgres$# product = a * b; postgres$# return next; postgres$# end; postgres$# $$language plpgsql; CREATE FUNCTION postgres=# select * from func_add_sql3(1, 2); sum | product -----+--------- 3 | 2 (1 row)
√ Oracle風格:
語法格式:
CREATE [ OR REPLACE ] FUNCTION function_name ( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ] } [, ...] ] ) RETURN rettype [ DETERMINISTIC ] [ {IMMUTABLE | STABLE | VOLATILE } | {SHIPPABLE | NOT SHIPPABLE} | {PACKAGE} | {FENCED | NOT FENCED} | [ NOT ] LEAKPROOF | {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } | {[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | AUTHID DEFINER | AUTHID CURRENT_USER } | COST execution_cost | ROWS result_rows | SET configuration_parameter { {TO | =} value | FROM CURRENT ][...] { IS | AS } plsql_body /
執行用例:
定義為Oracle的PL/SQL風格的函數:
實例1:
postgres=# CREATE FUNCTION func_add_sql2(a integer, b integer) RETURN integer postgres-# AS postgres$# BEGIN postgres$# RETURN a + b; postgres$# END; postgres$# / CREATE FUNCTION postgres=# call func_add_sql2(1, 2); func_add_sql2 --------------- 3 (1 row)
實例2:
postgres=# CREATE OR REPLACE FUNCTION func_add_sql3(a integer, b integer) RETURN integer postgres-# AS postgres$# sum integer; postgres$# BEGIN postgres$# sum := a + b; postgres$# return sum; postgres$# END; postgres$# / CREATE FUNCTION postgres=# call func_add_sql3(1, 2); func_add_sql3 --------------- 3 (1 row)
若想使用Oracle的PL/SQL風格定義OUT參數需要使用到存儲過程,請看下面章節。
存儲過程的創建
存儲過程與函數功能基本相似,都屬於過程化SQL語言,不同的是存儲過程沒有返回值。
※ 需要注意的是目前GaussDB(DWS)只支持Oracle的CREATE PROCEDURE的語法支持,暫時不支持PostgreSQL的CREATE PROCEDURE語法支持。
× PostgreSQL風格:
暫不支持。
√ Oracle風格:
語法格式:
CREATE [ OR REPLACE ] PROCEDURE procedure_name [ ( {[ argmode ] [ argname ] argtype [ { DEFAULT | := | = } expression ]}[,...]) ] [ { IMMUTABLE | STABLE | VOLATILE } | { SHIPPABLE | NOT SHIPPABLE } | {PACKAGE} | [ NOT ] LEAKPROOF | { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } | {[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | AUTHID DEFINER | AUTHID CURRENT_USER} | COST execution_cost | ROWS result_rows | SET configuration_parameter { [ TO | = ] value | FROM CURRENT } ][ ... ] { IS | AS } plsql_body /
執行用例:
postgres=# CREATE OR REPLACE PROCEDURE prc_add postgres-# ( postgres(# param1 IN INTEGER, postgres(# param2 IN OUT INTEGER postgres(# ) postgres-# AS postgres$# BEGIN postgres$# param2:= param1 + param2; postgres$# dbms_output.put_line('result is: '||to_char(param2)); postgres$# END; postgres$# / CREATE PROCEDURE postgres=# call prc_add(1, 2); result is: 3 param2 -------- 3 (1 row)
經過以上對GaussDB(DWS)過程化SQL語言的簡單介紹,我們大致了解了在GaussDB(DWS)中匿名塊,函數,存儲過程的創建,下面將簡單介紹一下在過程化SQL語言中的一些簡單的語法介紹。
基本語法介紹
賦值:
支持 = 與 := 兩種賦值符合的使用。下面兩種賦值方式都是支持的。
a = b; a := b + 1;
條件語句:
支持IF ... THEN ... END IF; IF ... THEN ... ELSE ... END IF; IF ... THEN ... ELSEIF ... THEN ... ELSE ... END IF;其中ELSEIF也可以寫成ELSIF。
語法介紹:
-- Case 1: IF 條件表達式 THEN --表達式為TRUE后將執行的語句 END IF; -- Case 2: IF 條件表達式 THEN --表達式為TRUE后將執行的語句 ELSE --表達式為FALSE后將執行的語句 END IF; -- Case 3: IF 條件表達式1 THEN --表達式1為TRUE后將執行的語句 ELSEIF 條件表達式2 THEN --表達式2為TRUE 后將執行的語句 ELSE --以上表達式都不為TRUE 后將執行的語句 END IF;
示例:
postgres=# CREATE OR REPLACE PROCEDURE pro_if_then(IN i INT) postgres-# AS postgres$# BEGIN postgres$# IF i>5 AND i<10 THEN postgres$# dbms_output.put_line('This is if test.'); postgres$# ELSEIF i>10 AND i<15 THEN postgres$# dbms_output.put_line('This is elseif test.'); postgres$# ELSE postgres$# dbms_output.put_line('This is else test.'); postgres$# END IF; postgres$# END; postgres$# / CREATE PROCEDURE postgres=# call pro_if_then(1); This is else test. pro_if_then ------------- (1 row) postgres=# call pro_if_then(6); This is if test. pro_if_then ------------- (1 row) postgres=# call pro_if_then(11); This is elseif test. pro_if_then ------------- (1 row)
循環語句:
支持while,for, foreach的使用。循環期間也可以適當添加循環控制語句continue, break。
語法介紹:
WHILE 條件表達式1 THEN --循環內需要執行的語句 END LOOP; FOR i IN result LOOP --循環內需要執行的語句 END LOOP; FOREACH var IN result LOOP --循環內需要執行的語句 END LOOP;
示例:
postgres=# CREATE OR REPLACE FUNCTION func_loop(a integer) RETURN integer postgres-# AS postgres$# sum integer; postgres$# var integer; postgres$# BEGIN postgres$# sum := a; postgres$# WHILE sum < 10 LOOP postgres$# sum := sum + 1; postgres$# END LOOP; postgres$# postgres$# RAISE INFO 'current sum: %', sum; postgres$# FOR i IN 1..10 LOOP postgres$# sum := sum + i; postgres$# END LOOP; postgres$# postgres$# RAISE INFO 'current sum: %', sum; postgres$# FOREACH var IN ARRAY ARRAY[1, 2, 3, 4] LOOP postgres$# sum := sum + var; postgres$# END LOOP; postgres$# postgres$# RETURN sum; postgres$# END; postgres$# / CREATE FUNCTION postgres=# call func_loop(1); INFO: current sum: 10 INFO: current sum: 65 func_loop ----------- 75 (1 row)
GOTO語句:
支持goto語法的使用。
語法介紹:
GOTO LABEL; --若干語句 <<label>>
示例:
postgres=# CREATE OR REPLACE FUNCTION goto_while_goto() postgres-# RETURNS TEXT postgres-# AS $$ postgres$# DECLARE postgres$# v0 INT; postgres$# v1 INT; postgres$# v2 INT; postgres$# test_result TEXT; postgres$# BEGIN postgres$# v0 := 1; postgres$# v1 := 10; postgres$# v2 := 100; postgres$# test_result = ''; postgres$# WHILE v1 < 100 LOOP postgres$# v1 := v1+1; postgres$# v2 := v2+1; postgres$# IF v1 > 25 THEN postgres$# GOTO pos1; postgres$# END IF; postgres$# END LOOP; postgres$# postgres$# <<pos1>> postgres$# /* OUTPUT RESULT */ postgres$# test_result := 'GOTO_base=>' || postgres$# ' v0: (' || v0 || ') ' || postgres$# ' v1: (' || v1 || ') ' || postgres$# ' v2: (' || v2 || ') '; postgres$# RETURN test_result; postgres$# END; postgres$# $$ postgres-# LANGUAGE 'plpgsql'; CREATE FUNCTION postgres=# postgres=# SELECT goto_while_goto(); goto_while_goto ------------------------------------------- GOTO_base=> v0: (1) v1: (26) v2: (116) (1 row)
異常處理:
語法介紹:
[<<label>>]
[DECLARE
declarations]
BEGIN
statements
EXCEPTION
WHEN condition [OR condition ...] THEN
handler_statements
[WHEN condition [OR condition ...] THEN
handler_statements
...]
END;
示例:
postgres=# CREATE TABLE mytab(id INT,firstname VARCHAR(20),lastname VARCHAR(20)) DISTRIBUTE BY hash(id); CREATE TABLE postgres=# INSERT INTO mytab(firstname, lastname) VALUES('Tom', 'Jones'); INSERT 0 1 postgres=# CREATE FUNCTION fun_exp() RETURNS INT postgres-# AS $$ postgres$# DECLARE postgres$# x INT :=0; postgres$# y INT; postgres$# BEGIN postgres$# UPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones'; postgres$# x := x + 1; postgres$# y := x / 0; postgres$# EXCEPTION postgres$# WHEN division_by_zero THEN postgres$# RAISE NOTICE 'caught division_by_zero'; postgres$# RETURN x; postgres$# END;$$ postgres-# LANGUAGE plpgsql; CREATE FUNCTION postgres=# call fun_exp(); NOTICE: caught division_by_zero fun_exp --------- 1 (1 row) postgres=# select * from mytab; id | firstname | lastname ----+-----------+---------- | Tom | Jones (1 row) postgres=# DROP FUNCTION fun_exp(); DROP FUNCTION postgres=# DROP TABLE mytab; DROP TABLE
總結:
GaussDB(DWS)對於過程化SQL語言的支持主要在PostgreSQL與Oracle上做了兼容,同時針對Oracle的一些高級包以及一些Oracle獨有的語法也做了一定支持。在遷移Oracle或者PostgreSQL時,對於函數或存儲過程的遷移可以減少為了兼容導致的額外工作量。
至此已經將GaussDB(DWS)中的匿名塊,函數,存儲過程的創建以及基本使用介紹的差不多了。當然GaussDB(DWS)對於過程化SQL語言的支持不止如此,在接下來的時間里,還將逐步向大家介紹游標,用戶自定義類型等章節喲~
想了解GuassDB(DWS)更多信息,歡迎微信搜索“GaussDB DWS”關注微信公眾號,和您分享最新最全的PB級數倉黑科技,后台還可獲取眾多學習資料哦~