すべてのプロダクト
Search
ドキュメントセンター

AnalyticDB:Oracle アプリケーションから AnalyticDB for PostgreSQL への移行

最終更新日:Mar 31, 2026

AnalyticDB for PostgreSQL は Oracle 構文と互換性があります。本ページでは、Oracle アプリケーションを AnalyticDB for PostgreSQL に移行する際に必要なすべての手順について説明します。具体的には、Ora2Pg を用いた移行範囲の評価方法、互換性モードの設定方法、Orafce 拡張機能を活用した Oracle 互換関数の利用方法、および PL/SQL 構文を PL/pgSQL に変換する方法について解説します。

主な違い一覧

具体的な変換ルールに着手する前に、以下の高レベルの違いを確認してください。これらは最も一般的な移行課題に影響を与えます。

エリア Oracle の動作 AnalyticDB for PostgreSQL の動作
NULL の連結 'abc' || NULL を返します 'abc' NULL を返します(PostgreSQL のデフォルト動作)。互換性モードを有効化することで動作を変更できます
ROWNUM 疑似列 LIMIT(行数制限)または row_number() over()(行番号付与)を使用します
DUAL テーブル 一部のクエリで必須となる仮想テーブル FROM DUAL を削除するか、dual テーブルを作成します
CONNECT BY ネイティブな階層クエリ 直接対応する構文はありません。PL/pgSQL 関数として再記述します
パッケージ CREATE PACKAGE パッケージはサポートされていません。スキーマ+関数へ変換します
PRAGMA コンパイル指示子 サポートされていません。すべての PRAGMA 文を削除します
関数内でのトランザクション制御 BEGINCOMMITROLLBACK を関数本体内で使用可能 サポートされていません。トランザクション制御は関数外で実行します
OUTER JOIN (+) WHERE a.id = b.id(+) 標準の LEFT JOINRIGHT JOIN 構文を使用します
FOR LOOP REVERSE 2 番目の数値から 1 番目の数値までカウントダウン 1 番目の数値から 2 番目の数値までカウントダウン。境界値を入れ替えます
RETURNOUT 引数を備えた関数 両方を同時に使用可能 サポートされていません。RETURN を追加の OUT 引数に変換します
連想配列 サポートされています 対応する機能はありません。設計の見直しが必要です
SELECT SQL ステートメントを含めることができます SQL ステートメントを含めることはできません。サブクエリとして再記述します

Ora2Pg を用いたスキーマ変換の自動化

Ora2Pg は、Oracle のテーブル、ビュー、パッケージに関する DDL ステートメントを PostgreSQL 互換構文に変換するオープンソースツールです。

まず移行範囲を評価

完全な変換を実行する前に、どのオブジェクトが自動変換できないかを特定するための移行評価レポートを生成します。

ora2pg -t SHOW_REPORT

このレポートには、すべてのデータベースオブジェクト、その数、および手動修正が必要なオブジェクトが一覧表示されます。移行を開始する前に、このレポートをもとに作業範囲を見積もります。

Ora2Pg が自動処理する項目

  • テーブルおよびビューの DDL

  • パッケージ構造(スキーマへ変換)

  • 基本的な PL/SQL から PL/pgSQL への構文変換

手動修正が必要な項目

Ora2Pg の実行後、以下の問題点を確認・修正し、ご利用のインスタンスに対してスクリプトを実行する前に検証を行ってください。

  • 変換されたスクリプトが、ご利用のインスタンスのマイナーエンジンバージョンよりも新しい PostgreSQL 構文バージョンをターゲットとしている可能性があります

  • 複雑な PL/SQL 構文については、変換ルールが不完全または不正確である可能性があります

  • 以下「PL/SQL 変換」セクションに記載されているすべての構文

互換性モードの設定

adb_compatibility_mode パラメーターは、データベース間で動作が異なる構文を AnalyticDB for PostgreSQL がどのように処理するかを制御します。

パラメーター 有効な値 デフォルト値
adb_compatibility_mode postgresoracle postgres

変更を行う前に、現在の値を確認します。

SHOW adb_compatibility_mode;

インスタンスレベルでこのパラメーターを変更するには、チケットを起票してください。チケットを起票

NULL を含む文字列連結

'abc' || NULL の動作は、互換性モードによって異なります。

  • PostgreSQL モード(デフォルト):NULL を返します

  • Oracle モード:'abc' を返します(Oracle では NULL が空文字列として扱われます)

重要

Oracle 互換モードで文字列連結を使用する前に、Laser エンジンを無効化します。

SET laser.enable = off;

定数文字列の型推論

AnalyticDB for PostgreSQL では、CREATE TABLE AS SELECT などのステートメントにおいて、定数文字列が自動的に TEXT 型(UNKNOWN ではなく)として認識されます。この動作は、互換性モードの設定を必要としません。

Orafce 拡張機能の利用

Orafce 拡張機能は、AnalyticDB for PostgreSQL で Oracle 互換関数を変更や変換なしに利用できるようにします。

Orafce はデータベースごとに 1 回インストールします。

CREATE EXTENSION orafce;

Orafce は、Oracle の VARCHAR2 データの型もサポートします。

日時関数

関数 説明
add_months(day date, value int) 日付に指定月数を加算します SELECT add_months(current_date, 2);2019-08-31
last_day(value date) 当月の最終日を返します SELECT last_day('2018-06-01');2018-06-30
next_day(value date, weekday text) 次の平日を返します SELECT next_day(current_date, 'FRIDAY');
next_day(value date, weekday integer) 曜日を整数で指定:1=日曜日、2=月曜日、…、7=土曜日 SELECT next_day('2019-06-22', 1);2019-06-23
months_between(date1 date, date2 date) 2 つの日付間の月数を返します(date1 > date2 の場合、正の値) SELECT months_between('2019-01-01', '2018-11-01');2

タイムスタンプの切り捨ておよび丸め

関数 説明
trunc(value timestamp with time zone, fmt text) 年(Y)、四半期(Q)、月、日、週、時、分、秒単位で切り捨てます SELECT TRUNC(current_date, 'Q');2019-04-01
trunc(value timestamp with time zone) 時、分、秒を切り捨てます SELECT TRUNC('2019-12-11'::timestamp);2019-12-11 00:00:00+08
trunc(value date) 日付を切り捨てます SELECT TRUNC('2019-12-11'::timestamp, 'Y');2019-01-01 00:00:00+08
round(value timestamp with time zone, fmt text) 指定単位(週、日など)で最も近い値に丸めます SELECT round('2018-10-06 13:11:11'::timestamp, 'YEAR');2019-01-01 00:00:00+08
round(value timestamp with time zone) 最も近い日に丸めます SELECT round('2018-10-06 13:11:11'::timestamp);2018-10-07 00:00:00+08
round(value date, fmt text) 丸められた日付を返します SELECT round(TO_DATE('27-OCT-00','DD-MON-YY'), 'YEAR');2001-01-01
round(value date) 丸められた日付を返します SELECT round(TO_DATE('27-FEB-00','DD-MON-YY'));2000-02-27

NULL 処理関数

関数 説明
nvl(anyelement, anyelement) 第 1 引数が NULL の場合、第 2 引数を返します。それ以外の場合は第 1 引数を返します。両引数は同じ型である必要があります。 SELECT nvl(null, 1);1
nvl2(anyelement, anyelement, anyelement) 第 1 引数が NULL の場合、第 3 引数を返します。それ以外の場合は第 2 引数を返します SELECT nvl2(null, 1, 2);2
lnnvl(bool) 引数が NULL または false の場合、true を返します。引数が true の場合、false を返します SELECT lnnvl(null);t
nanvl(float4, float4)nanvl(numeric, numeric) 第 1 引数が NaN の場合、第 2 引数を返します。それ以外の場合は第 1 引数を返します SELECT nanvl('NaN', 1.1);1.1

文字列関数

関数 説明
instr(str text, patt text, start int, nth int) 文字列 str 内のパターン patt の n 番目の出現位置を、start から開始して返します。見つからない場合は 0 を返します。
instr(str text, patt text, start int) start
instr(str text, patt text) 文字列の先頭から検索します
substr(str text, start int) start
substr(str text, start int, len int) 部分文字列を返します。lenstart 以上かつ文字列長以下である必要があります
pg_catalog.substrb(varchar2, integer, integer) VARCHAR2 文字列の指定位置から終了位置までの部分文字列を返します
pg_catalog.substrb(varchar2, integer) VARCHAR2 文字列の指定位置から末尾までの部分文字列を返します
pg_catalog.lengthb(varchar2) VARCHAR2 文字列のバイト数を返します。入力が NULL の場合は NULL、空文字列の場合は 0 を返します
lpad(string char, length int, fill char) 文字列を指定長に左詰めします。
説明

PostgreSQL では CHAR 値の末尾スペースが削除されますが、Oracle では削除されません。

lpad(string char, length int) スペースで左詰めします
concat(text, text) 2 つの文字列を結合します
concat(text/anyarray, text/anyarray) 同一または異なる型の値を結合します
nlssort(text, text) ロケール固有の順序(例:'en_US.UTF-8''C')でデータを並べ替えます
plvstr.rvrs(str text, start int, end int) start から end
plvstr.rvrs(str text, start int) start から文字列末尾までの文字を反転します
plvstr.rvrs(str text) 文字列全体を反転します

集計関数

関数 説明
listagg(text) 値を 1 つの文字列に連結します SELECT listagg(t) FROM (VALUES('abc'), ('def')) as l(t);abcdef
listagg(text, text) 区切り文字を指定して連結します SELECT listagg(t, '.') FROM (VALUES('abc'), ('def')) as l(t);abc.def
bitand(bigint, bigint) 2 つの整数値に対するビット演算 AND を実行します SELECT bitand(2, 6);2

診断関数

関数 説明
dump("any") 型コード、バイト長、内部表現を返します SELECT dump('adb4pg');Typ=705 Len=7: 97,100,98,52,112,103,0
dump("any", integer) 出力形式を指定します:10(10 進数)、16(16 進数)。2 を指定するとエラーになります。 SELECT dump('adb4pg', 16);

正規表現関数

以下のすべての正規表現関数は、flags 引数を受け入れます。有効な値は次のとおりです:'i'(大文字小文字を区別しない)、'c'(大文字小文字を区別する)、'n'. が改行にもマッチ)、'm'(マルチライン)、'x'(空白を無視)。

関数 説明
regexp_count(string, pattern) pattern の出現回数を返します。一致しない場合は 0 を返します
regexp_count(string, pattern, startPos) startPos
regexp_count(string, pattern, startPos, flags) カスタムマッチングフラグでカウントします
regexp_instr(string, pattern) パターンの開始位置を返します。一致しない場合は 0 を返します
regexp_instr(string, pattern, startPos, occurrence, return_opt, flags, group) 完全なシグネチャ:return_opt=0 は一致の開始位置を返し、return_opt=1 は一致後の位置を返します。group はキャプチャグループを指定します(0 = 全体一致)
regexp_like(string, pattern) 任意の部分文字列がパターンに一致する場合、true を返します
regexp_like(string, pattern, flags) カスタムマッチングフラグで true を返します
regexp_substr(string, pattern) 最初に一致した部分文字列を返します
regexp_substr(string, pattern, startPos, occurrence, flags) startPos

Orafce を使用せずに利用可能な関数

以下の Oracle 互換関数は、Orafce のインストールを必要とせず、AnalyticDB for PostgreSQL でネイティブに利用可能です。

関数 説明
sinh(float) 双曲線正弦 SELECT sinh(0.1);0.100166750019844
tanh(float) 双曲線正接 SELECT tanh(3);0.99505475368673
cosh(float) 双曲線余弦 SELECT cosh(0.2);1.02006675561908
decode(expression, value, return [,value,return]... [,default]) 一致する値を返します。一致しない場合はデフォルト値を返します 以下の例を参照してください
-- サンプルテーブルの作成
CREATE TABLE t1(id int, name varchar(20));
INSERT INTO t1 values(1,'alibaba');
INSERT INTO t1 values(2,'adb4pg');

-- decode:id=1 の場合 'alibaba'、id=2 の場合 'adb4pg' を返します
SELECT decode(id, 1, 'alibaba', 2, 'adb4pg', 'not found') FROM t1;

データ型のマッピング

Oracle の型 AnalyticDB for PostgreSQL の型
VARCHAR2 varchar または text
DATE timestamp
LONG text
LONG RAW bytea
CLOB text
NCLOB text
BLOB bytea
RAW bytea
ROWID oid
FLOAT double precision
DEC decimal
DECIMAL decimal
DOUBLE PRECISION double precision
INT int
INTEGER integer
REAL real
SMALLINT smallint
NUMBER numeric
BINARY_FLOAT double precision
BINARY_DOUBLE double precision
TIMESTAMP timestamp
XMLTYPE xml
BINARY_INTEGER integer
PLS_INTEGER integer
TIMESTAMP WITH TIME ZONE timestamp with time zone
TIMESTAMP WITH LOCAL TIME ZONE timestamp with time zone

関数のマッピング

Oracle 関数 AnalyticDB for PostgreSQL における同等の関数
sysdate current_timestamp
trunc trunc または date_trunc
dbms_output.put_line RAISE ステートメント
decode CASE WHEN または decode
NVL coalesce

PL/SQL におけるデータの変換

PL/SQL(Procedural Language/SQL)は、AnalyticDB for PostgreSQL では PL/pgSQL に対応します。以下では、各 PL/SQL 構文の変換ルールについて説明します。

パッケージ

PL/pgSQL ではパッケージはサポートされていません。各パッケージをスキーマに変換し、パッケージ内のすべてのプロシージャおよび関数を、そのスキーマ内のスタンドアロン関数に変換します。

Oracle:

CREATE OR REPLACE PACKAGE pkg IS
    ...
END;

AnalyticDB for PostgreSQL:

-- パッケージはスキーマになります
CREATE SCHEMA pkg;

パッケージ内容の変換ルール:

  • プロシージャおよび関数内のローカル変数:変更は不要です

  • グローバル変数:一時テーブルに格納します(下記「グローバル変数」を参照)

  • パッケージ初期化ブロック:削除します。初期化ロジックを削除できない場合は、関数にカプセル化し、必要に応じて明示的に呼び出します。

  • プロシージャおよび関数:対応するスキーマ内の関数に変換します。各関数にはスキーマ名をプレフィックスとして付与します。

例 — パッケージ内の関数:

Oracle:

FUNCTION test_func (args int) RETURN int is
var number := 10;
BEGIN
    ...
END;

AnalyticDB for PostgreSQL:

-- RETURN → RETURNS;IS/AS → AS $$...$$;LANGUAGE 句を追加
CREATE OR REPLACE FUNCTION pkg.test_func(args int) RETURNS int AS
$$
    ...
$$
LANGUAGE plpgsql;

プロシージャおよび関数

Oracle のプロシージャおよび関数を PL/pgSQL 関数に変換します。主な変更点は以下のとおりです。

  • 関数シグネチャ内の RETURNRETURNS

  • 関数本体を $$...$$ で囲みます

  • LANGUAGE plpgsql 句を追加します

  • サブプロシージャをスタンドアロン関数に変換します

例:

Oracle:

CREATE OR REPLACE FUNCTION test_func (v_name varchar2, v_version varchar2)
RETURN varchar2 IS
    ret varchar(32);
BEGIN
    IF v_version IS NULL THEN
        ret := v_name;
    ELSE
        ret := v_name || '/' || v_version;
    END IF;
    RETURN ret;
END;

AnalyticDB for PostgreSQL:

-- 変更点:varchar2 → varchar;RETURN → RETURNS;IS → AS;本体を $$...$$ で囲みます
CREATE OR REPLACE FUNCTION test_func (v_name varchar, v_version varchar)
RETURNS varchar AS
$$
DECLARE
    ret varchar(32);
BEGIN
    IF v_version IS NULL THEN
        ret := v_name;
    ELSE
        ret := v_name || '/' || v_version;
    END IF;
    RETURN ret;
END;
$$
LANGUAGE plpgsql;

PL ステートメント

REVERSE を伴う FOR LOOP

PL/SQL と PL/pgSQL では、REVERSE キーワードの動作が異なります。

  • PL/SQL:FOR i IN REVERSE 1..3 は 3 から 1 へカウントダウンします

  • PL/pgSQL:FOR i IN REVERSE 1..3 は 1 から 3 へカウントダウンします(逆方向)

変換時にループ境界値を入れ替えます。

Oracle:

FOR i IN REVERSE 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE(TO_CHAR(i));
END LOOP;

AnalyticDB for PostgreSQL:

-- 境界値を入れ替え:1..3 → 3..1;DBMS_OUTPUT.PUT_LINE → RAISE
FOR i IN REVERSE 3..1 LOOP
    RAISE '%', i;
END LOOP;

PRAGMA 文

PRAGMA 文はサポートされていません。すべての PRAGMA 文を削除します。

トランザクション制御

AnalyticDB for PostgreSQL の関数では、関数本体内で BEGINCOMMITROLLBACK を使用できません。以下のいずれかの戦略を適用します。

  • 関数本体からトランザクション制御文を削除し、関数呼び出しの外側で実行します。

  • COMMIT または ROLLBACK の境界ごとに、関数を個別の関数に分割します。

EXECUTE(動的 SQL)

AnalyticDB for PostgreSQL では動的 SQL がサポートされますが、Oracle との違いがあります。

  • USING 構文はサポートされていません。パラメーターを SQL 文字列に直接連結します。

  • データベース識別子は quote_ident で、文字列値は quote_literal で囲みます。

Oracle:

EXECUTE 'UPDATE employees_temp SET commission_pct = :x' USING a_null;

AnalyticDB for PostgreSQL:

-- USING を quote_literal を使った文字列連結に置き換え
EXECUTE 'UPDATE employees_temp SET commission_pct = ' || quote_literal(a_null);

PIPE ROW 関数

PIPE ROW 関数は、RETURNS SETOF <type> および RETURN NEXT を使用したテーブル関数に置き換えます。

Oracle:

TYPE pair IS RECORD(a int, b int);
TYPE numset_t IS TABLE OF pair;

FUNCTION f1(x int) RETURN numset_t PIPELINED IS
DECLARE
    v_p pair;
BEGIN
    FOR i IN 1..x LOOP
        v_p.a := i;
        v_p.b := i+10;
        PIPE ROW(v_p);
    END LOOP;
    RETURN;
END;

SELECT * FROM f1(10);

AnalyticDB for PostgreSQL:

-- RECORD 型 → CREATE TYPE;PIPE ROW → RETURN NEXT;PIPELINED → RETURNS SETOF
CREATE TYPE pair AS (a int, b int);

CREATE OR REPLACE FUNCTION f1(x int) RETURNS SETOF pair AS
$$
DECLARE
    rec pair;
BEGIN
    FOR i IN 1..x LOOP
        rec := row(i, i+10);
        RETURN NEXT rec;
    END LOOP;
    RETURN;
END
$$
LANGUAGE plpgsql;

SELECT * FROM f1(10);

例外処理

  • 例外をスローするには RAISE ステートメントを使用します。

  • 例外がキャッチされた後、関数内ではトランザクションをロールバックできません。ロールバックはユーザー定義関数の外側でのみ許可されます。

サポートされるエラーコードについては、「PostgreSQL エラーコード リファレンス」をご参照ください。

RETURN および OUT 引数を備えた関数

関数は、RETURN 引数と OUT 引数を同時に使用できません。RETURN 引数を追加の OUT 引数に変換し、SELECT * FROM test_func(...) INTO rec を使用して戻り値を取得します。

Oracle:

CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int) RETURNS varchar(10)
AS $body$
BEGIN
    out_id := id + 1;
    RETURN name;
END
$body$
LANGUAGE PLPGSQL;

AnalyticDB for PostgreSQL:

-- RETURN 値を OUT 引数(out_name)に変換
CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int, out_name out varchar(10))
AS $body$
BEGIN
    out_id := id + 1;
    out_name := name;
END
$body$
LANGUAGE PLPGSQL;

-- 両方の出力値を取得
SELECT * FROM test_func(1, '1') INTO rec;

動的 SQL 文字列連結におけるシングルクォート

文字列変数にシングルクォートが含まれている場合(例:adb'-'pg)、直接連結すると、ハイフンが演算子と解釈されて構文解析エラーが発生します。quote_literal を使用して値を安全に埋め込みます。

Oracle:

sql_str := 'SELECT * FROM test1 WHERE col1 = ' || param1 || ' AND col2 = ''' || param2 || ''' AND col3 = 3';

AnalyticDB for PostgreSQL:

-- quote_literal により param2 が安全にラップされ、埋め込まれたクォートが処理されます
sql_str := 'SELECT * FROM test1 WHERE col1 = ' || param1 || ' AND col2 = ' || quote_literal(param2) || ' AND col3 = 3';

2 つのタイムスタンプ間の日数

Oracle:

SELECT to_date('2019-06-30 16:16:16') - to_date('2019-06-29 15:15:15') + 1 INTO v_days FROM dual;

AnalyticDB for PostgreSQL:

-- extract() を使用して間隔から整数日数を取得
SELECT extract('days' FROM '2019-06-30 16:16:16'::timestamp - '2019-06-29 15:15:15'::timestamp + '1 days'::interval)::int INTO v_days;

PL データ型

RECORD 型

Oracle の RECORD 型は、複合型に変換します。

Oracle:

TYPE rec IS RECORD (a int, b int);

AnalyticDB for PostgreSQL:

CREATE TYPE rec AS (a int, b int);

ネストテーブルおよび可変サイズ配列

PL 変数として、NESTED TABLE および VARRAY は、PostgreSQL の ARRAY 型に対応します。

Oracle:

DECLARE
    TYPE Roster IS TABLE OF VARCHAR2(15);
    names Roster := Roster('D Caruso', 'J Hamil', 'D Piro', 'R Singh');
BEGIN
    FOR i IN names.FIRST .. names.LAST LOOP
        IF names(i) = 'J Hamil' THEN
            DBMS_OUTPUT.PUT_LINE(names(i));
        END IF;
    END LOOP;
END;

AnalyticDB for PostgreSQL:

-- TABLE OF → varchar[];配列インデックス:names(i) → names[i];FIRST..LAST → 1..array_length
CREATE OR REPLACE FUNCTION f1() RETURNS VOID AS
$$
DECLARE
    names varchar(15)[] := '{"D Caruso", "J Hamil", "D Piro", "R Singh"}';
    len int := array_length(names, 1);
BEGIN
    FOR i IN 1..len LOOP
        IF names[i] = 'J Hamil' THEN
            RAISE NOTICE '%', names[i];
        END IF;
    END LOOP;
    RETURN;
END
$$
LANGUAGE plpgsql;

SELECT f1();

ネストテーブルが関数の戻り値として使用される場合は、テーブル関数(RETURNS SETOF <type>)を使用します。

連想配列

AnalyticDB for PostgreSQL には Oracle の連想配列に対応する機能はありません。この構文は設計の見直しが必要です。

グローバル変数

AnalyticDB for PostgreSQL ではグローバル変数はサポートされていません。パッケージレベルのグローバル変数は一時テーブルに格納し、アクセサ関数を定義します。

-- グローバル変数を一時テーブルに格納
-- 注:id 列は分散キーであり、変更できません
CREATE TEMPORARY TABLE global_variables (
    id int,
    g_count int,
    g_set_id varchar(50),
    g_err_code varchar(100)
);

INSERT INTO global_variables VALUES(0, 1, null, null);

-- ゲッター関数
CREATE OR REPLACE FUNCTION get_variable() RETURNS SETOF global_variables AS
$$
DECLARE
    rec global_variables%rowtype;
BEGIN
    EXECUTE 'SELECT * FROM global_variables' INTO rec;
    RETURN NEXT rec;
END;
$$
LANGUAGE plpgsql;

-- セッター関数
CREATE OR REPLACE FUNCTION set_variable(IN param varchar(50), IN value anyelement) RETURNS void AS
$$
BEGIN
    EXECUTE 'UPDATE global_variables SET ' || quote_ident(param) || ' = ' || quote_literal(value);
END;
$$
LANGUAGE plpgsql;

グローバル変数を変更するには:

-- 呼び出し元関数内で tmp_rec レコードを宣言
SELECT * FROM set_variable('g_err_code', 'error'::varchar) INTO tmp_rec;

グローバル変数を読み取るには:

SELECT * FROM get_variable() INTO tmp_rec;
error_code := tmp_rec.g_err_code;

SQL 変換

CONNECT BY

階層クエリのための CONNECT BY 句は、AnalyticDB for PostgreSQL には直接対応する SQL 構文がありません。階層を反復的に走査する PL/pgSQL 関数として再記述します。

Oracle:

SELECT emp_id, lead_id, emp_name, PRIOR emp_name AS lead_name, salary
FROM employee
START WITH lead_id = 0
CONNECT BY PRIOR emp_id = lead_id;

AnalyticDB for PostgreSQL(反復的走査関数):

CREATE OR REPLACE FUNCTION f1(tablename text, lead_id int, nocycle boolean) RETURNS SETOF employee AS
$$
DECLARE
    idx int := 0;
    res_tbl varchar(265) := 'result_table';
    prev_tbl varchar(265) := 'tmp_prev';
    curr_tbl varchar(256) := 'tmp_curr';
    current_result_sql varchar(4000);
    tbl_count int;
    rec record;
BEGIN
    EXECUTE 'TRUNCATE ' || prev_tbl;
    EXECUTE 'TRUNCATE ' || curr_tbl;
    EXECUTE 'TRUNCATE ' || res_tbl;
    LOOP
        -- 現在の階層レベルをクエリし、tmp_curr に挿入
        current_result_sql := 'INSERT INTO ' || curr_tbl || ' SELECT t1.* FROM ' || tablename || ' t1';

        IF idx > 0 THEN
            current_result_sql := current_result_sql || ', ' || prev_tbl || ' t2 WHERE t1.lead_id = t2.emp_id';
        ELSE
            current_result_sql := current_result_sql || ' WHERE t1.lead_id = ' || lead_id;
        END IF;
        EXECUTE current_result_sql;

        -- nocycle が false の場合、既に走査済みの行を削除
        IF nocycle IS FALSE THEN
            EXECUTE 'DELETE FROM ' || curr_tbl || ' WHERE (lead_id, emp_id) IN (SELECT lead_id, emp_id FROM ' || res_tbl || ')';
        END IF;

        -- 行がない場合に終了
        EXECUTE 'SELECT count(*) FROM ' || curr_tbl INTO tbl_count;
        EXIT WHEN tbl_count = 0;

        -- 現在の結果を result および prev テーブルに昇格
        EXECUTE 'INSERT INTO ' || res_tbl || ' SELECT * FROM ' || curr_tbl;
        EXECUTE 'TRUNCATE ' || prev_tbl;
        EXECUTE 'INSERT INTO ' || prev_tbl || ' SELECT * FROM ' || curr_tbl;
        EXECUTE 'TRUNCATE ' || curr_tbl;
        idx := idx + 1;
    END LOOP;

    FOR rec IN EXECUTE 'SELECT * FROM ' || res_tbl LOOP
        RETURN NEXT rec;
    END LOOP;
    RETURN;
END
$$
LANGUAGE plpgsql;

ROWNUM

結果件数の制限:

Oracle:

SELECT * FROM t WHERE rownum < 10;

AnalyticDB for PostgreSQL:

SELECT * FROM t LIMIT 10;

行番号の生成:

Oracle:

SELECT rownum, * FROM t;

AnalyticDB for PostgreSQL:

SELECT row_number() OVER() AS rownum, * FROM t;

DUAL テーブル

オプション 1 — `FROM DUAL` を削除:

Oracle:

SELECT sysdate FROM dual;

AnalyticDB for PostgreSQL:

SELECT current_timestamp;

オプション 2 — `dual` テーブルを作成:

CREATE TABLE dual (dummy varchar(1));
INSERT INTO dual VALUES ('X');

SELECT ステートメント内のユーザー定義関数

AnalyticDB for PostgreSQL では、SQL ステートメントを含むユーザー定義関数を SELECT 内で呼び出すことはできません。実行するとセグメントエラーが発生します。

ERROR: function cannot execute on segment because it accesses relation "public.t2"

このような関数は、SQL 式またはサブクエリに変換します。

Oracle:

CREATE OR REPLACE FUNCTION f1(arg int) RETURN int IS
    v int;
BEGIN
    SELECT b INTO v FROM t2 WHERE a = arg;
    RETURN v;
END;

SELECT a, f1(b) FROM t1;

AnalyticDB for PostgreSQL:

-- 関数ロジックを JOIN としてインライン化
SELECT t1.a, t2.b FROM t1, t2 WHERE t1.b = t2.a;

OUTER JOIN (+)

2 テーブルの外部結合:

Oracle:

SELECT * FROM a, b WHERE a.id = b.id(+);

AnalyticDB for PostgreSQL:

SELECT * FROM a LEFT JOIN b ON a.id = b.id;

3 テーブルの外部結合:

まず 2 つのテーブルを CTE(共通テーブル式)で結合し、その後 RIGHT OUTER JOINcoalesce を適用します。

Oracle:

SELECT * FROM test1 t1, test2 t2, test3 t3
WHERE t1.col1(+) BETWEEN NVL(t2.col1, t3.col1) AND NVL(t3.col1, t2.col1);

AnalyticDB for PostgreSQL:

WITH cte AS (
    SELECT t2.col1 AS low, t2.col2, t3.col1 AS high, t3.col2 AS c2
    FROM t2, t3
)
SELECT * FROM t1
RIGHT OUTER JOIN cte ON t1.col1 BETWEEN coalesce(cte.low, cte.high) AND coalesce(cte.high, cte.low);

MERGE INTO

ほとんどの場合、MERGE INTOINSERT ON CONFLICT に置き換えます。INSERT ON CONFLICT で対応できない場合は、ストアドプロシージャを使用します。

シーケンス

Oracle:

CREATE SEQUENCE seq1;
SELECT seq1.nextval FROM dual;

AnalyticDB for PostgreSQL:

CREATE SEQUENCE seq1;
SELECT nextval('seq1');

カーソル

基本的なカーソル走査 はサポートされています。OPEN/FETCH/CLOSE パターンを使用します。

Oracle:

FUNCTION test_func() IS
    CURSOR data_cursor IS SELECT * FROM test1;
BEGIN
    FOR i IN data_cursor LOOP
        -- process i
    END LOOP;
END;

AnalyticDB for PostgreSQL:

CREATE OR REPLACE FUNCTION test_func()
AS $body$
DECLARE
    data_cursor CURSOR FOR SELECT * FROM test1;
    i record;
BEGIN
    OPEN data_cursor;
    LOOP
        FETCH data_cursor INTO i;
        IF NOT FOUND THEN
            EXIT;
        END IF;
        -- process i
    END LOOP;
    CLOSE data_cursor;
END;
$body$
LANGUAGE PLPGSQL;

再帰関数内での同名カーソル はサポートされていません。代わりに FOR i IN <query> を使用します。

Oracle:

FUNCTION test_func(level IN number) IS
    CURSOR data_cursor IS SELECT * FROM test1;
BEGIN
    IF level > 5 THEN RETURN; END IF;
    FOR i IN data_cursor LOOP
        -- process i
        test_func(level + 1);
    END LOOP;
END;

AnalyticDB for PostgreSQL:

-- 同名カーソルを削除;再帰をサポートするためにインラインクエリを使用
CREATE OR REPLACE FUNCTION test_func(level int) RETURNS void
AS $body$
DECLARE
    i record;
BEGIN
    IF level > 5 THEN
        RETURN;
    END IF;
    FOR i IN SELECT * FROM test1 LOOP
        -- process i
        PERFORM test_func(level + 1);
    END LOOP;
END;
$body$
LANGUAGE PLPGSQL;

よくある落とし穴

これらのランタイム動作の違いは、移行後のバグの最も頻繁な原因となります。変換済みコードのテストを実施する前に、必ず確認してください。

落とし穴 Oracle の動作 AnalyticDB for PostgreSQL の動作 対応策
文字列連結における NULL 'abc' || NULL'abc' NULL Oracle 互換モードに切り替えるか、coalesce
FOR LOOP REVERSE の方向 2 番目の数値から 1 番目の数値へカウントダウン 1 番目の数値から 2 番目の数値へカウントダウン ループ境界値を入れ替えます
関数内でのトランザクション制御 COMMITROLLBACK が関数本体内で許可されています サポートされていません 関数外に移動するか、関数を分割します
USING EXECUTE '...' USING var USING はサポートされていません quote_literalquote_ident
RETURN および OUT 両方とも許可されています サポートされていません RETURNOUT 引数に変換します
SELECT テーブルをクエリできます テーブルをクエリできません サブクエリまたは JOIN として再記述します
連想配列 サポートされています 対応する機能はありません 設計の見直しが必要です
再帰における同名カーソル サポートされています サポートされていません FOR i IN <query> を使用します

次のステップ