1. 程式人生 > >pgsql使用積累系列_pg儲存過程(函式)建立(以手機號校驗為例)

pgsql使用積累系列_pg儲存過程(函式)建立(以手機號校驗為例)

      pg資料庫裡面沒有地道的儲存過程(procudure),取而代之的是函式。函式的編寫支援多種語言,底層的庫函式通常是用C語言編寫的,應用級別函式可用sql和pgsql。

下面通過一個通用的手機號清洗的函式,來展示一個函式的級別要素,通用函式構造方案,及postgresql中正則表示式的使用和拼接包含字串的字串方法。整個函式的功能,就是通過傳入的表名,表主鍵及需要清洗的手機號欄位,將不符合要求的的手機號洗為空字串,之所以不洗成null,是因為null在有些情況下會給人造成困擾(比如selece帶條件比較時資料跟預期不一致),該函式只能解決小寫的引數。下面詳解

CREATE OR REPLACE FUNCTION "public"."check_phone"("table_name" varchar, "table_id" varchar, "phone" varchar)
  RETURNS "pg_catalog"."void" AS $BODY$

DECLARE
    exec_str VARCHAR(1024);

BEGIN
	RAISE NOTICE 'notice: %', 'begin check';
	exec_str = 'create temp table temp_phone on commit drop as
	select ' || table_id || ',' || '
		case when ' || phone || E' ~ \'^[+]\' then replace(' || phone || E', \'+\', \'00\') else ' || phone || ' end as ' || phone || '
	from ' || table_name || ';' || '

	create temp table temp_phone_1 on commit drop as
	select ' || table_id || ',' || '
		case when ' || phone || E' ~ \'\\D\' then \'\'
			when ' || phone || E' is null then \'\'
			when ' || phone || E' ~ \'^1\' and length(' || phone || ') = 11 then ' || phone || '
			when ' || phone || E'~ \'^01\' and length(' || phone || ') = 12 then ' || phone || '
			when ' || phone || E'~ \'^861\' and length(' || phone || ') = 13 then ' || phone || '
			when ' || phone || E'~ \'00861\' and length(' || phone || ') = 15 then ' || phone || E'
		else \'\' end as ' || phone || '
	from temp_phone;

	update ' || table_name || '  a set ' || phone || ' = t.' || phone || '
	from (select ' || table_id || ', ' || phone || ' from temp_phone_1) t
	where a.' || table_id || ' = t.' || table_id || ';';

	RAISE NOTICE 'notice: %', exec_str;
	execute exec_str;
END;

$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100

CREATE OR REPLACE FUNCTION "public"."check_phone"():在指定的模式下建立一個函式名為check_phone的函式,如果函式已經存在就更新它。

"table_name" varchar, "table_id" varchar, "phone" varchar :函式的引數,預設是in (函式的引數分為in,out,inout,這個大部分關係型資料庫都一樣)。

RETURNS "pg_catalog"."void":返回型別,在postgres裡,一切皆物件,型別和表一樣可以自定義,也有對應的模式。

AS :表示下面的內容等價於前面的定義。可以理解成上面是定義,下面是實現。

$BODY$:函式的主題。可以理解成html頁面裡的body,是實際功能的載體。

DECLARE exec_str VARCHAR(1024) :申明一個待執行的sql字串。

BEGIN...END:其中的內容在一個事務內。其實有的儲存過程不加這個也是可以的,只是多個語句不會在一個事務內執行。

RAISE NOTICE 'notice: %', 'begin check':列印日誌,%是佔位符。

create temp table temp_phone on commit drop as :建立臨時表,並在事務提交時自動刪除,as跟上面的AS是一個意思。

E' ~ \'^[+]\' then replace(':E使字串內的轉義字元'\'生效。~ 代表正則表示式,匹配字串中首字母是'+'的字串。

E' ~ \'\\D\' then \'\':'\D'匹配一個非數字。

execute exec_str:執行sql串

 LANGUAGE plpgsql:表示用的是pgsql語言。

VOLATILE:表示函式操作物件為最大,即可以操作表,資料庫等等。這個是預設值,無需可以配置。如果控制儲存過程的使用範圍需要調整這個值。

COST 100:這個引數會影響執行計劃。也是預設值,不建議修改。

因為postgresql欄位區分大小寫,而且查詢時會將大寫欄位預設改成小寫,如果資料庫欄位為大寫,則會出現找不到欄位的錯誤,所以建議表結構最好用小寫,多個單詞用下劃線連線,而不是用駝峰結構。當然也不是完全解決不了大小寫問題的,可以在欄位或者表等元素前後加上"號來阻止大寫自動轉為小寫。

下面提供一個同樣功能但是能適配大寫欄位名,表名的函式程式碼,除了支援大寫的情況,這段程式碼還展示了另一種處理字元拼接中包含字串的問題,上段程式碼是通過正則匹配解決,這段程式碼是通過在原有字串外再套一層單引號(')解決的。

CREATE OR REPLACE FUNCTION "public"."check_phone"("table_name" varchar, "table_id" varchar, "phone" varchar)
  RETURNS "pg_catalog"."void" AS $BODY$


DECLARE
    exec_str VARCHAR(1024);

BEGIN
	RAISE NOTICE 'notice: %', 'begin check';
	exec_str = 'create temp table temp_phone on commit drop as
	select "' || table_id || '",' || '
		case when "' || phone || '" ~ ''^[+]'' then replace("' || phone || '", ''+'', ''00'') else "' || phone || '" end as "' || phone || '"
	from "' || table_name || '";' || '

	create temp table temp_phone_1 on commit drop as
	select "' || table_id || '",' || '
		case when "' || phone || '" ~ ''\D'' then ''''
			when "' || phone || '" is null then ''''
			when "' || phone || '" ~ ''^1'' and length("' || phone || '") = 11 then "' || phone || '"
			when "' || phone || '" ~ ''^01'' and length("' || phone || '") = 12 then "' || phone || '"
			when "' || phone || '" ~ ''^861'' and length("' || phone || '") = 13 then "' || phone || '"
			when "' || phone || '" ~ ''00861'' and length("' || phone || '") = 15 then "' || phone || '"
		else '''' end as "' || phone || '"
	from temp_phone;

	update "' || table_name || '"  a set "' || phone || '" = t."' || phone || '"
	from (select "' || table_id || '", "' || phone || '" from temp_phone_1) t
	where a."' || table_id || '" = t."' || table_id || '";';

	RAISE NOTICE 'notice: %', exec_str;
	execute exec_str;
END;


$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100