[PostgreSQL] 我的PL/pgSQL授課用的範例-進銷存(一)-進貨篇
沒想到這裡我已有12年沒更新了. 現在重回來寫Blog看看, 慢慢再清理這裡的佈局和內容.
走在這條資訊管理的不歸路, 不知不覺30年了, 一路全在業界打滾, 有得有失, 心境也隨著修行, 世事無常, 留下點微薄的經驗貢獻給需要的人.
全文連結在: https://oss-tw.blogspot.com/2021/02/postgresql-plpgsql.html
全球最先進的開放源碼資料庫系統! PostgreSQL 以全球最自由且不受用途限制的 BSD 授權協議發佈, 擁有龐大的全球開發者/用戶社群, PostgreSQL 為企業級應用特性亦完整且高度相容於 ISO/ANSI SQL 國際標準, 高可用性(HA)及數據儲存品質倫美與持續超越 Oracle, DB2... 等商業型資料庫管理系統, TOC(整體擁有成本)更遠低於商業型 DBMS 達數百倍.
沒想到這裡我已有12年沒更新了. 現在重回來寫Blog看看, 慢慢再清理這裡的佈局和內容.
走在這條資訊管理的不歸路, 不知不覺30年了, 一路全在業界打滾, 有得有失, 心境也隨著修行, 世事無常, 留下點微薄的經驗貢獻給需要的人.
全文連結在: https://oss-tw.blogspot.com/2021/02/postgresql-plpgsql.html
更新:2007-09-09
對映章節:
內容:
使用 PL/pgSQL 不外乎就是撰寫資料庫的預儲程序(Stored Procedure)、函數(Function)、觸發器(Trigger) 等。設計這些內部程序,其中除了迴圈、真假值判斷、回傳等等特別需求的語法外,主要的內容還是由各種 SQL 查詢命令或是呼叫其他已存在的函數所構成。由於使用 PL/pgSQL 呼叫其他函數或執行SQL命令時,多半是要等待回傳值、資料列並更進一步處理,所以與平時用前端程式或SQL命令列對函數與SQL指令的操作上,有比較不同的習慣性用法。要了解在 PL/pgSQL 處理各種呼叫查詢,必須從回傳值處理的角度去深入。
以下是幾個常用處理函數和SQL命令回傳值的方法:
SELECT * INTO rs FROM mytable;
然後可讀取回傳資料列的各欄位內容:
rs.id
rs.name
rs.address
...
註:此 SELECT INTO 使用方法很特別,並非是你想的那樣,請參考下文說明。
SELECT address INTO addr FROM mytable;
EXECUTE myfunction();
PERFORM myfunction();
註:EXECUTE 和 PERFORM 的詳細差異不在本文討論範圍,請參考官方說明文件。
在資料整理時,我們常碰到有些資料需要往前移動,有些則要往後移動,尤其在資料的顯示流程規劃上就常常要使用到這種 功能。可是傳統資料庫的排序功能,不外乎就是使用日期、時間、資料編號做為排序依據,然後利用 ORDER BY 命令達成資料排序的目的,除此之外,我們無法動態調整資料列的先後次序。
一種解決辦法是為每個資料列增加新的欄位﹝例子中取名為mytable_after﹞,用來記錄該筆資料列的前一筆資料編號。所以我們在讀取資料時就使用該欄位記錄的值,逐一列出每筆資料。操作流程大致上是:
讀取第一筆資料 A﹝mytable_after 為 0﹞
→ 讀取欄位 mytable_after = A 的第二筆資料 B→ 重覆操作直到找不到下一筆資料
PostgreSQL 動態排列實作例子:
CREATE OR REPLACE FUNCTION mytable_dynlist()
RETURNS SETOF mytable AS
$BODY$DECLARE
rscount Integer;
rsnext Integer;
rs record;
BEGINselect count(*) INTO rscount from mytable;
rsnext = 0;
FOR i IN 1 .. rscount LOOP
SELECT * INTO rs from mytable where mytable_after = rsnext LIMIT 1;
rsnext = rs.mytable_id;
RETURN NEXT rs;
END LOOP;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION mytable_dynlist() OWNER TO somebody;
當 資料不多時,這種在資料庫內部處理動態排列很省事,在前端的程式也很好寫,但是,資料量一大,這種方式會造成 SQL Server 相當大的負擔。所以,當資料量多或是在前端程式有做多台機器的負載平衡,還是建議在前端先將所有要排序的資料下載後,在前端的程式做排序處理。
Trigger - 觸發器,與交易資料鎖定一樣,是商用資料庫系統不可或缺的重要功能之一。你可曾要插入資料到資料庫的時候,同時又需要連帶更新其他表單的資料?在過去,你一定也是在前端應用程式,使用 INSERT INTO 然後再用 UPDATE,或是用一句句的 INSERT 命令來完成這任務。許多諸如此類的同步操作,假若都在資料庫系統外一步步下達命令完成,不僅是程式設計上繁複,效能上也不彰。觸發器的功能就是為了解決這類問題而設計的,當你更新或查詢某個資料表時會觸動觸發器,觸發器就會照您所設計的流程,同步去插入、更新、刪除其他資料,你不再需要重復下達多次的 SQL命令就能達成一連串資料的同步處理。
深入了解觸發器函數之前,如果能有個情境會比較好了解其中運作,你可以先想像一下有兩個資料表 mytable 和 mytable_counter 還有其中的各項欄位如下:
如果現在要設計一個機制統計每月客戶數量,你會怎麼做呢?以往傳統做法應該都是在 INSERT INTO mytable 之後,再 UPDATE mytable_counter。可是今天我們要把 UPDATE 的動作寫成觸發器函數,讓日後只要我們 INSERT 資料進去 mytable,PostgreSQL就會自動同步幫我們去 UPDATE mytable_counter 的 mc_count欄位,不需要我們再另外下達 UPDATE 的指令。
以下是一個簡單的觸發器函數例子:
CREATE OR REPLACE FUNCTION pg_mytrigger()
RETURNS "trigger" AS
$BODY$BEGIN
UPDATE mytable_counter SET mc_count = (mc_count + 1) WHERE mc_mytable_id = NEW.mytable_id;
RETURN NEW;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION pg_mytrigger() OWNER TO somebody;
其中要注意的,NEW.mytable_id 代表新增加到 mytable 的資料列,其 mytable_id 欄位。在這例子中用來找到當月的統計紀錄,並予以統計數量加一。
套用觸發器函數
到此,觸發器函數到這階段已經建立好了,下一步要決定套用在哪一個資料表,也就是我們要設定哪一個資料表被 INSERT 時,PostgreSQL會去更新統計表的記錄。我們可以用以下指令套用觸發器函數到目標資料表上:
CREATE TRIGGER pg_mytrigger
BEFORE INSERT
ON mytable
FOR EACH ROW
EXECUTE PROCEDURE pg_mytrigger();
這 段命令需要解釋的是 BEFORE 這參數,觸發器雖說是『同時』照你的流程執行,但實際上是分為 BEFORE 和 AFTER,差別在於 BEFORE 是在『更新、查詢資料表之前』執行觸發器預訂的函數,而 AFTER 是在『之後』執行。也就是 BEFORE是先執行觸發器再新增或刪除客戶資料,AFTER是先新增、刪除資料再去執行觸發器。
後記
上 面的例子只是為了說明觸發器 (Trigger) 的功用和使用方法,已經簡化了很多內容,實際上還有許多需要考慮的地方,例如當統計資料表找不到當月的統計資料怎麼辦﹝因為該客戶可能是當月第一個客戶 ﹞?所以必須還要修改觸發器函數去判斷修正這一部份。另外一方面,目前只談到 INSERT 時執行觸發器函數,我們也可以在 DELETE 時執行觸發器函數為統計數量減一,這些都是觸發器可以做得到的,以後再花時間進一步討論。
標籤: 觸發器(Trigger), PL/pgSQL
PostgreSQL 資料分頁 Stored Procedure 實作例子:CREATE OR REPLACE FUNCTION mytable_list(ps bigint, page bigint)
RETURNS SETOF mytable AS
$BODY$DECLARE
rs mytable;
BEGIN
FOR rs IN select * from mytable LIMIT ps OFFSET ps*(page-1) LOOP
RETURN NEXT rs;
END LOOP;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION mytable_list(ps bigint, page bigint) OWNER TO somebody;
延伸閱讀(Link):ps - 設定每一頁的資料列數量page - 指定目前讀取的頁碼
增進 SQL 處理效率的一個很重要的方法就是使用 Stored Procedure ,一般商用級以上的資料庫系統都會內建這種功能。
以下是在 PostgreSQL 中建立一個 Stored Procedure 的最簡單例子:
-- 執行結果:執行 select * from mytable where table_id = id 並回傳所有符合的資料列
CREATE OR REPLACE FUNCTION pg_myfunc(id integer)
RETURNS SETOF mytable AS
$BODY$DECLARE
rs RECORD;
BEGIN
FOR rs IN select * from mytable where table_id = id LOOP
RETURN NEXT rs;
END LOOP;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION pg_myfunc(id integer) OWNER TO somebody;
如同 Oracle PL/SQL一般,PostgreSQL也有自己的程序語言 PL/pgSQL,其中最常用到的幾種語法使用:
IF 判斷式
IF ... THEN
ELSIF ... THEN
ELSE
ENDIF;
FOR LOOP迴圈
FOR i IN 1 .. 10 LOOP
...
END LOOP;
當找不到任何符合的資料列時
SELECT * FROM mytable WHERE mytable_id = id;
IF not found THEN
-- 找不到任何符合的資料列時執行這裡的程序END IF;
全部的 Function 都建立完成了,接著就要開始使用已建立的 Stored Procedure。
執行 Functions 有兩種SQL命令可以使用:
Function 沒有回傳值時使用 SELECT pg_myfunc();
Function 有回傳值時使用 SELECT * FROM pg_myfunc();
更新:2007-03-28
對映章節:
http://www.faqs.org/docs/ppbook/c19610.htm
內容:
PL/pgSQL 是一個可載入的程式語言
因為歷史性及開發者大多從 Oracle 轉入關係, 導致二者語法相似度非常的高, 對學習者也較易於在 Oracle PL/SQL 與 PostgreSQL PL/pgSQL 間互換。
您能使用 PL/pgSQL 群組序列化 SQL 和架構模式(Schema)性宣告式在資料庫伺服器之內, 減少在網路和通信上, 由客戶端應用導致經常必須請求資料從資料庫和進行邏輯操作資料從一個遠程位置。
您 得以進入對所有 PostgreSQL 資料類型、運算子, 和作用的在 PL/pgSQL 代碼之內。
"SQL" 在 PL/pgSQL 是表示的事實您被允許直接地使用 SQL 語言從 PL/pgSQL 代碼內部。對SQL 的用途在 PL/pgSQL 代碼之內可能增加您的能力、靈活性, 和效能。如果多個SQL 聲明被執行從PL/pgSQL 代碼區塊, 聲明一次被處理, 代替處理一個唯一聲明的正常作用一次。
其它使用 PL/pgSQL 的重要方面是它的輕便; 它的作用是與可能操作 PostgreSQL 資料庫系統的所有平臺相容。
以下部分描述怎麼使 PL/pgSQL 可被利用作為一個程式語言在您的資料庫中。
安裝 PL/pgSQL 在 template1 資料庫會導致被創造與 template1 的所有隨後新創資料庫都當是預設值, 因為的他們的模板資料庫有 PL/pgSQL 被安裝。
原文作者簡介:
Josh Berkus 在 2002年加入 PostgreSQL 的核心開發成員. 現在他在 Sun Microsystems 的開放源碼資料庫團隊中擔任領導者.
擁有 11 年的資料庫經驗, 同時亦曾工作於其它專案包括: OpenOffice.org, Microsoft SQL Server, Oracle PL/SQL, and (shudder) COM+...
更新:2007-03-27
對映章節:PostgreSQL Application Performance Tips
內容:
這是一篇由 Josh Berkus 撰寫的好文章, 阿益翻譯分享給大家參考:
為 PostgreSQL 性能的應用程序設計
原文作者簡介:
Josh Berkus 在 2002年加入 PostgreSQL 的核心開發成員. 現在他在 Sun Microsystems 的開放源碼資料庫團隊中擔任領導者.
擁有 11 年的資料庫經驗, 同時也曾經工作於其它社群專案包括: OpenOffice.org, Microsoft SQL Server, Oracle PL/SQL, and (shudder) COM+...
更新:2007-03-25
對映章節:PostgreSQL Application Performance Tips
內容:
這是一篇由 Josh Berkus 撰寫的好文章, 阿益翻譯分享給大家參考:
為 PostgreSQL 性能的應用程序設計
查詢撰寫規則
所有資料庫管理系統(DBMSes), "往返"(round-trip)時間是很寶貴的。這是因為它取得 Query 回來時必須通過語言分析器(language parser)、驅動器, 穿透網路介面, 資料庫分析器(database parser), 計劃器, 執行器, 反譯分析器, 再返回穿透網路介面, 通過驅動資料處理器, 和對 Client 應用程序的時間。
DBMSes 總是在時間上跟 CPU 爭取進程來處理這個循環週期, 並且由於各式各樣的原因每次在"往返"(round-trip) 上, PostgreSQL 是很嚴苛在意時間和系統資源。
更進一步, PostgreSQL 有著重要特點一切全是事務交易(per-transaction), 每筆事務交易處理都包括日誌記錄的產出和各種需要被設置的可見性規則在內。當您認為您可以不使用事務交易處理下的單一唯讀(singleton read-only) SELECT 的宣告, 實際上每一個宣告對 PostgreSQL 都是在進行事務交易處理。在沒有一種明確事務交易處理時, 宣告也是一種隱藏事務交易處理。
補充這, PostgreSQL 是唯一不輸給 Oracle 在處理大型複雜的查詢式(queries)上, 並且有能力輕易地處理複雜多宣告(multi-statement)式的事務交易在併發同作時的衝突處理。我們並且支持游標(cursors), 包括可捲動式(scrollable)與非捲動式(non-scrollable)。
更新:2007-03-21
對映章節:C37.11
內容:
本節解釋了 Oracle 的 PL/SQL 和 PostgreSQL 的 PL/pgSQL語言之間的差別, 希望能對那些從Oracle® 向 PostgreSQL 移植應用的人有所幫助。
PL/pgSQL與 PL/SQL 在許多方面都非常類似。 它是一種塊結構的,命令式的語言並且必須宣告所有變量。 賦值,循環,條件等都很類似。 在從 Oracle 向 PostgreSQL 移植的時候必須記住一些事情:
在PostgreSQL中參數沒有預設值。
你可以在PostgreSQL裡重載函數。 這個特性常用於繞開缺乏預設參數的問題。
在PL/pgSQL裡不需要游標(CURSOR), 只要把命令放在 FOR 語句裡就可以了
在 PostgreSQL 裡,函數體必須寫成字串文本, 因此你需要使用美元符包圍或者逃逸函數體裡面的單引號。
我們應該用模式把函數組織成不同的群組,而不是用 package。
因為沒有 package,所以也沒有 package 級別的變量。這一點有時候挺討厭。 你可以在臨時表裡保存會話級別的狀態
更新:2007-03-14
對映章節:C37.8
內容:
一般的 SELECT 使用 LIMIT 來限制取回 Data 的數量, 其餘的 Queries 均會回傳整個搜尋結果.
但當資料量龐大時回傳會造成本上升, 且可能導致 Client 的 RAM 耗盡. CURSOR 就在這情況被用來解決上述的問題.
條件:

更新:2007-03-04
對映章節:II,C7
內容:
PostgreSQL 支持表的結合(Joined)種類有五種:


標籤: 技術教學與認證, GUI管理工具與操作, PL/pgSQL
更新:2007-02-27
對映章節:II,9.3
今天來介紹幾個實用的
數學函數(Mathematical Functions)與運算子(Mathematical Operators), 詳細的還是請看官方文檔.
準確度和數值範圍方面的行為是根據宿主系統而變化的。
| 運算子 | 詳述 | 例子 | 結果 |
|---|---|---|---|
| % | 模除 (求余) | 5 % 4 | 1 |
| ^ | 冪(指數運算) | 2.0 ^ 3.0 | 8 |
| ! | 階乘 | 5 ! | 120 |
| @ | 絕對值 | @ -5.0 | 5 |
| & | 二進制 AND | 91 & 15 | 11 |
| 函數 | 返回類型 | 描述 | 例子 | 結果 |
|---|---|---|---|---|
| abs(x) | (和x類型相同) | 絕對值 | abs(-17.4) | 17.4 |
| ceil(dp 或者 numeric) | 與輸入相同 | 不小於參數的最小的整數 | ceil(-42.8) | -42 |
| ceiling(dp or numeric) | (與輸入相同) | 不小於參數的最小整數(ceil 的別名) | ceiling(-95.3) | -95 |
| floor(dp 或 numeric) | 與輸入相同 | 不大於參數的最大整數 | floor(-42.8) | -43 |
| mod(y, x) | (和參數類型相同) | 除法 y/x 的餘數(模) | mod(9,4) | 1 |
| random() | dp | 0.0 到 1.0 之間的隨機數值 | random() | |
| round(dp 或者 numeric) | (與輸入相同) | 圓整為最接近的整數 | round(42.4) | 42 |
| round(v numeric, s integer) | numeric | 圓整為s位小數數字 | round(42.4382, 2) | 42.44 |
| setseed(dp) | integer | 為隨後的 random() 調用設置種子 | setseed(0.54823) | 1177314959 |
| trunc(dp 或者 numeric) | (和輸入相同) | 截斷(向零靠近)(truncate) | trunc(42.8) | 42 |
| trunc(v numeric, s integer) | numeric | 截斷為 s 小數位置的數字 | trunc(42.4382, 2) | 42.43 |
|
|
| PostgreSQL 8 DBA 專業指南中文版 |
| 書籍內容討論與更多下載區(造訪此群組)
目錄下載: PostgreSQL_8 _DBA_Index_zh_TW.pdf (更新:2007-05-18) |