版 | 改訂日 | 変更内容 |
---|---|---|
1.0 | 2013/03/25 | 新規作成 |
2.0 | 2014/03/26 | 2013 年度活動成果の追加 |
3.0 | 2018/03/16 |
|
4.0 | 2020/03/17 | PostgreSQLの対象バージョンを11.6に更新
|
本作品はCC-BYライセンスによって許諾されています。 ライセンスの内容を知りたい方は こちら でご確認ください。 文書の内容、表記に関する誤り、ご要望、感想等につきましては、PGEConsのサイト を通じてお寄せいただきますようお願いいたします。
本資料は、異種DBMSからPostgreSQLへストアドプロシージャを移行する作業の難易度およびボリュームの事前判断と、実際に書き換えを行う際の参考資料として利用されることを想定しています。
本資料では、移行元の異種DBMSとしてOracle Databaseを想定し、PostgreSQLへストアドプロシージャを移行する際に書き換えが必要である箇所とその書き換え方針について手続き言語を中心に記載します。スキーマ、SQL、組み込み 関数については本資料では取り扱っていません。
これらに関しては、それぞれ「スキーマ移行調査編」、「SQL移行調査編」、「組み込み関数移行調査編」を参照してください。
データベースに対する一連の処理手順をまとめてDBMS内に格納する「ストアドプロシージャ」について、PostgreSQLにおける特徴を紹介します。
Oracle | PostgreSQL |
---|---|
CREATE OR REPLACE PROCEDURE
プロシージャ名 (引数名 IN データ型)
IS
変数名 データ型;
BEGIN
処理内容;
END [プロシージャ名];
|
CREATE OR REPLACE PROCEDURE
プロシージャ名 (引数名 IN データ型)
AS $$
DECLARE
変数名 データ型;
BEGIN
処理内容;
END;
$$
LANGUAGE plpgsql;
|
IS → ASOUT → INOUTEND [プロシージャ名]; → END;
Oracle | PostgreSQL |
---|---|
CREATE OR REPLACE PACKAGE パッケージ名
IS
PROCEDURE プロシージャ名 (
(後略)
|
CREATE SCHEMA IF NOT EXISTS スキーマ名;
CREATE OR REPLACE PROCEDURE スキーマ名.プロシージャ名 (
(後略)
|
OracleとPostgreSQLにそれぞれ実装されている手続き言語である、PL/SQLとPL/pgSQLにおける記述の相違を中心に書換え方法を記述します。
構造のステートメントには相違ありません。
DECLARE
変数名 データ型;
BEGIN
処理内容
END;
コメントの記述には相違ありません。
-- コメント記述 :行末までをコメントとします。/* コメント記述 */ :/* から */ までのブロック(複数行でも可)をコメントとします。
INOUT引数プロシージャの呼び出し方
<INOUT引数を持つプロシージャ>
CREATE OR REPLACE PROCEDURE プロシージャ名(変数名 INOUT データ型)
...
<呼び出し元の宣言部>
変数 データ型;
<呼び出し部分>
CALL プロシージャ名(変数);
Oracle | PostgreSQL |
---|---|
CREATE OR REPLACE PROCEDURE プロシージャ名 (
変数名 データ型 := デフォルト値
...
|
CREATE OR REPLACE PROCEDURE プロシージャ名 (
変数名 IN データ型 = デフォルト値
...
|
RECORD型については注意が必要です。
Oracle | PostgreSQL |
---|---|
type 変数名 is RECORD (
変数名 データ型
);
|
変数名 RECORD;
|
Oracle | PostgreSQL |
---|---|
変数 := TO_NUMBER(値);
変数 := TO_CHAR(値);
|
変数 := 値::numeric;
変数 := 値::text;
|
LOOPの記述には相違ありません。
LOOP
繰り返し処理;
EXIT WHEN 条件式;
END LOOP;
WHILEの記述には相違ありません。
WHILE 条件式 LOOP
繰り返し処理;
END LOOP;
FORの記述には相違ありません。
FOR 変数名 IN 1 .. 10 LOOP
繰り返し処理;
END LOOP;
Oracle | PostgreSQL |
---|---|
FOR 変数名 IN REVERSE 1 .. 10 LOOP
繰り返し処理;
END LOOP;
|
FOR 変数名 IN REVERSE 10 .. 1 LOOP
繰り返し処理;
END LOOP;
|
EXITの記述には相違ありません。
EXIT;
EXIT [ ラベル名 ] ;
EXIT WHEN A1 > 10;
CONTINUEの記述には相違ありません。
CONTINUE;
CONTINUE [ ラベル 名 ] ;
CONTINUE WHEN 条件式;
CASEの記述には相違ありません。
CASE 変数
WHEN 条件値 THEN
分岐処理
ELSE
分岐処理
END CASE;
PostgreSQLにはGOTO命令がありません。
Oracle | PostgreSQL |
---|---|
GOTO ラベル ;
|
[対応する命令なし] |
カーソルの宣言については注意が必要です。
Oracle | PostgreSQL |
---|---|
CURSOR カーソル名 IS クエリー;
|
カーソル名 CURSOR FOR クエリー;
|
Oracle | PostgreSQL |
---|---|
CURSOR カーソル名 (引数 IN データ型) IS ...
|
カーソル名 CURSOR (引数 データ型) FOR ...
|
カーソルのFETCHの記述には相違ありません。
FETCH カーソル名 INTO 取得した値を格納する変数;
カーソルから行を1行づつ取り出して変数に格納します。
カーソルをすべてFETCHしたときの判定方法は注意が必要です。
Oracle | PostgreSQL |
---|---|
カーソル名%NOTFOUND;
|
NOT FOUND;
|
Oracleでは、カーソル名を明示して終了判定(NOTFOUND)しますが、PostgreSQLではカーソル名の指定はできません。
カーソルのカレント行に対する更新の記述には相違ありません。
<更新>
UPDATE テーブル名 SET 更新内容 WHERE CURRENT OF カーソル名;
<削除>
DELETE FROM テーブル名 WHERE CURRENT OF カーソル名;
カーソルの宣言時にFOR UPDATEを使って作成したカーソルの現在行に対して項目の値の変更およびレコードの削除を行います。
カーソルのCLOSEの記述には相違ありません。
CLOSE カーソル名;
BEGIN
CLOSE カーソル名;
EXCEPTION
WHEN invalid_cursor_name THEN NULL;
END;
CREATE OR REPLACE PROCEDURE プロシージャ名 ()
AS $$
DECLARE
カーソル名 refcursor;
BEGIN
(処理)
END;
EXCEPTIONの記述には相違ありません。
EXCEPTION
WHEN エラーコード(もしくは例外名) 1 THEN エラー処理内容1
WHEN エラーコード(もしくは例外名) 2 THEN エラー処理内容2
WHEN OTHERS THEN エラー処理内容3
END;
Oracleの例外名 | PostgreSQLの例外名 | 相違 |
---|---|---|
CASE_NOT_FOUND | CASE_NOT_FOUND | 同じ |
INVALID_CURSOR | INVALID_CURSOR_STATE | 書換え必要 |
NO_DATA_FOUND | NO_DATA_FOUND | 同じ*1 |
STORAGE_ERROR | OUT_OF_MEMORY | 書換え必要 |
TOO_MANY_ROWS | TOO_MANY_ROWS | 同じ |
ZERO_DIVIDE | DIVISION_BY_ZERO | 書換え必要 |
RAISEを使った例外を発生させる記述には相違ありません。
RAISE exception;
Oracle | PostgreSQL |
---|---|
EXECUTE プロシージャ名
|
CALL プロシージャ名();
|
(text型のINOUT引数を持つプロシージャを呼ぶ場合)
CREATE OR REPLACE PROCEDURE プロシージャ名()
AS $$
DECLARE
変数 text;
BEGIN
CALL 呼び出すプロシージャ名(変数);
END;
$$
LANGUAGE plpgsql;
例えば、以下のような場合はCOMMIT部分でエラーになります。
CREATE OR REPLACE PROCEDURE プロシージャ名(...)
AS $$
BEGIN
処理1
COMMIT; /* エラー */
処理2
EXCEPTION
ROLLBACK; /* 正常実行可能 */
例外処理
END;
$$
LANGUAGE plpgsql;
また、他にも以下のような違いがあります。
プロシージャが入れ子になっていて、親プロシージャにEXCEPTION句がある場合は、トランザクション制御を含む子プロシージャを実行部(BEGINブロック)から呼ぶことはできません。
nextval('シーケンス名') -- 次の値を取り出す
setval('シーケンス名', 値) -- 値をセットする
currval('シーケンス名') -- 現在値を再度取り出す
(初期化用関数例)
CREATE OR REPLACE FUNCTION スキーマ名.初期化用ファンクション名 ()
RETURNS void LANGUAGE plpgsql
AS $$
DECLARE
変数 INTEGER := 0;
BEGIN
SELECT INTO 変数 count(*) FROM information_schema.tables WHERE table_name = lower('一時テーブル名');
IF 変数 = 0 THEN
CREATE TEMP TABLE 一時テーブル名 (key TEXT, val TEXT);
INSERT INTO 一時テーブル名 VALUES
('パッケージ変数名1','値1'),
('パッケージ変数名2','値2'),
...;
END IF;
END;
$$;
(登録用関数)
CREATE OR REPLACE FUNCTION スキーマ名.登録用ファンクション名 (登録する変数 TEXT, 登録する値 TEXT)
RETURNS void LANGUAGE plpgsql
AS $$
DECLARE
更新する一時テーブル名 TEXT := '一時テーブル名';
変数 INTEGER := 0;
BEGIN
PERFORM スキーマ名.初期化用ファンクション名 ();
EXECUTE 'SELECT count(*) FROM ' || quote_ident(更新する一時テーブル名) || ' WHERE key = $1' INTO 変数 USING 登録する変数;
IF 変数 = 0 THEN
EXECUTE 'INSERT INTO ' || quote_ident(更新する一時テーブル名) || ' VALUES ($1, $2)' USING 登録する変数, 登録する値;
ELSE
EXECUTE 'UPDATE ' || quote_ident(更新する一時テーブル名) || ' SET val = $1 WHERE key = $2' USING 登録する値, 登録する変数;
END IF;
END;
$$;
(取得用関数)
CREATE OR REPLACE FUNCTION スキーマ名.取得用ファンクション名 (値を取得したい変数 TEXT)
RETURNS TEXT LANGUAGE plpgsql
AS $$
DECLARE
取得する一時テーブル名 TEXT := '一時テーブル名';
取得した値用変数 TEXT := NULL;
BEGIN
PERORM スキーマ名.初期化用ファンクション名 ();
EXECUTE 'SELECT val FROM ' || quote_ident(取得する一時テーブル名) || ' WHERE key = $1' INTO 取得した値用変数 USING 値を取得したい変数;
RETURN 取得した値用変数;
END;
$$;
版 | 所属企業・団体名 | 部署名 | 氏名 |
---|---|---|---|
ストアドプロシージャ移行調査編
第2版
(2013年度 WG2)
|
クオリカ株式会社 | 開発センター | 坂本 浩行 |
インフォメーションクリエーティブ株式会社 | ソリューション開発本部 | 林田 竜一 | |
ストアドプロシージャ移行調査編
第3版
(2017年度 WG2)
|
SRA OSS, Inc. 日本支社 | OSS 事業本部 | 佐藤 友章 |
SRA OSS, Inc. 日本支社 | OSS 事業本部 | 千田 貴大 | |
ストアドプロシージャ移行調査編
第4版
(2019年度 WG2)
|
富士通株式会社 | ソフトウェア事業本部 | 佐藤 光洋 |
富士通株式会社 | ソフトウェア事業本部 | 西垣 雅樹 | |
富士通株式会社 | ソフトウェア事業本部 | 豊島 良美 |