====== oracleからの移行 ======
OracleからPostgreSQLの移行について下記にまとめる。
===== 変更対象のデータ型、関数 =====
==== データ型 ====
* テーブル作成の際に変更が必要なデータ型を下記に記す。
^Oracleのデータ型^PostgreSQL^説明^備考^
|VARCHAR2(n)|CHARACTER VARYING(n)| 最大n文字の長さの文字列|Oracleはバイト数、PostgreSQLは文字数|
|NUMBER|NUMERIC|最大1000桁、ユーザ指定精度| |
|DATE|TIMESTAMP| |DATEのままだと日付のみになる|
|SYSDATE|current_timestamp \\ ※SQL92に従いnow()は使わない|現在の日付/時刻を取得する関数|HH24MMSS → HH24MISS|
|ROWID|oid|32bit(約43億)で一周してしまう|Create文でWITH OIDSと設定しないと使用できない、PostgreSQL12以降はWITH OIDS廃止|
| |ctid|ROWIDに相当|バキューム処理によって ctid が変更される可能性があります。|
参照:[[http://interdbconnect.sourceforge.net/oracle_fdw/data_mapping-ja.html|oracle_fdw データ型マッピング]]
=== 注意点 ===
OracleはVARCHAR2はバイト数、PostgreSQLはCHARACTER VARYINGは文字数となる。\\
OracleのNUMBER型をPostgreSQLのNUMERIC型に単純に変更したが、これには弊害もある。\\
整数しか入らないなら、素直にsmallint型やinteger型にする方がいいかも。
弊害としてキャストエラーによりプログラムの修正が発生した。\\
OracleではNumber型で整数のみだったため(int)でキャストしていたが、PostgreSQLではNUMERIC型だとDecimal型となることによりDataRowを使用した場合、(int)ではキャストエラーになってしまった。よって、Convert.ToInt32でキャストするように修正した。\\
参照:[[https://kkkw.hatenablog.jp/entry/20091021/1256121130|DataRowから値を取り出すときにdecimalにcastできない]]
==== 関数 ====
* テーブル作成の際に変更が必要な関数を下記に記す。
^Oracle演算子^PostgreSQLの演算子^説明^備考^
| NVL(文字列,変換文字列) | COALESCE(文字列,変換文字列) | 文字列がNULLの場合は、変換文字列を返す、NULLでなければ文字列を返す。|
| INSTR(文字列,検索文字列) | POSITION(検索文字列,IN 文字列) | 文字列から検索文字列で始まる位置を返す。 |
| DECODE( expr, cmp_expr1, ret_expr1, \\ cmp_expr2, ret_expr2, \\ default_expr) | CASE expr \\ WHEN cmp_expr1 THEN ret_expr1 \\ WHEN cmp_expr2 THEN ret_expr2 \\ ELSE default_expr END | exprがcmp_expr1と同値ならret_expr1を返す、cmp_expr2と同値ならret_expr2を返す、それ以外ならdefault_exprを返す。 |
| SELECT t1.key1, t1.name1, t2.name2 \\ FROM table t1, table t2 \\ WHERE t1.key1 = t2.key2(+) | SELECT t1.key1, t1.name1, t2.name2 \\ FROM table t1 LEFT OUTER JOIN table t2 \\ ON t1.key1 = t2.key | t1の列key1にしか存在しないデータを検索 |
=== SUBSTRについて ====
Oracleにて、SELECT SUBSTR('123456', -4, 4) FROM DUALを実行すると、「3456」が取得できるが、PostgreSQLでは、SELECT SUBSTR('123456',-4, 4)を実行すると、nullが取得できる。\\
PostgreSQLでは、SELECT RIGHT('123456', 4)で、Oracleと同じ結果が得られる。\\
第二パラメータが負の場合(右から取得しようとしている)のみ注意が必要。\\
第二パラメータが正の場合はどちらも同じ結果が得られるので注意しなくてもよい。
第二パラメータの開始インデックスが「0」のとき、Oracleでは開始インデックス「1」と同じだが、PostgreSQLでは前1文字分が削られる。\\
※第二パラメータの開始インデックスは「1」からと思えばいい。\\
[[http://heppoen.seesaa.net/article/149908257.html|[データベース比較]部分文字列取得SUBSTRの罠]]
Oracle : SELECT SUBSTR('ABCDE',0,3) FROM DUAL -> 'ABC'
PostgreSQL : SELECT SUBSTR('ABCDE',0,3) -> 'AB'
=== 文字列結合 ====
SELECT句で Null文字列を含むカラムを連結すると、全体が Null になります。\\
これを回避するためには、カラムごとに Null の場合の置換を指定することで対処可能です。\\
[[http://d.hatena.ne.jp/zankey/20070914/postgresql|[PostgreSQL] PostgreSQL の SELECT句で Null文字列を連結する方法]]
SELECT ok_column || '-' || coalesce((null_column, '') FROM foo;
=== to_char/to_number は cast に ===
>PostgreSQLでのto_char/to_number関数は第2引数が必須のため、省略しているとエラーになってしまいます。
>もちろん第2引数を指定する形に統一してもよいのですが、単純な数値→文字/文字→数値変換の場合、かえって記述が面倒なので、cast関数に置き換えてしまうのが手っ取り早いかと。
>「cast(value as varchar(255))」、「cast(value as numeric)」といった感じでほぼ統一できると思います。
[[http://www.meeks.jp/archives/4541|PostgreSQLへの移行]]
==== 仕様 ====
仕様差は機能差よりも厄介である。
=== 除算に誤差がでる ===
SQL内で除算を含む計算が実装されている場合、答えが一致しない。これはバグではなく仕様である。\\
誤差を許容するか、許容できない場合はSQL内で計算せずに計算結果のみをDBに格納するような対応をする。
^Oracle^PostgreSQL^
|1÷3×3=1|1÷3×3=0.999…|
|1/3+1/3+1/3=1|1/3+1/3+1/3=0.999…|
=== トランザクションの結果が異なる ===
複数トランザクションを使用した場合、トランザクションの同時実行の結果が異なる。
* [[https://thinkit.co.jp/article/1063/1|PostgreSQLとOracle Databaseのアーキテクチャ構造]]
* [[https://active.nikkeibp.co.jp/atcl/act/19/00050/082900006/|Oracle DBとPostgreSQLで異なる処理結果、「仕様差」問題に要注意]]
^時系列^Oracle^^PostgreSQL^^
^ ^トラン1^トラン2^トラン1^トラン2^
|処理1|SQL>select * from test_t; \\ COL1:COL2 \\ 1:1 \\ 2:1 \\ 3:2 \\ 4:2 \\ 5:2 \\ 5行が選択されました。|SQL>select * from test_t; \\ COL1:COL2 \\ 1:1 \\ 2:1 \\ 3:2 \\ 4:2 \\ 5:2 \\ 5行が選択されました。|test01=# select * from test_t; \\ COL1:COL2 \\ 1:1 \\ 2:1 \\ 3:2 \\ 4:2 \\ 5:2 \\ (5行)|test01=# select * from test_t; \\ COL1:COL2 \\ 1:1 \\ 2:1 \\ 3:2 \\ 4:2 \\ 5:2 \\ (5行)|
|処理2|SQL>update test_t set col2=col2+1; \\ 5行が更新されました。| |test01=# begin; \\ BEGIN \\ update test_t set col2=col2+1; UPDATE 5|
|処理3| |SQL>delete from test_1 where col2=3; \\ ※トラン1が更新のためロック開放待ち| |test01=# begin; \\ BEGIN \\ delete from test_1 where col2=3; \\ ※処理2の実行が確定していないので処理1の状態をターゲットに更新を保留|
|処理4|SQL>commit; \\ コミットが完了しました。| |test01=#commit; \\ COMMIT| |
|処理5| |2行が削除されました。 \\ SQL>commit; \\ コミットが完了しました。 \\ ※トラン1のロックが開放されて処理3のSQLを処理2の結果に対して実行| |DELETE 0 \\ test01=# commi; \\ COMMIT \\ ※トラン1のロックが開放されて処理3のSQLを実行しようとするが、処理2を実行した結果、処理1の5行目が処理3の実行条件を満たさないので、削除されなかった|
|処理6|SQL>select * from test_t; \\ COL1:COL2 \\ 1:2 \\ 2:2 \\ 5:4 \\ 3行が選択されました。| |test01=# select * from test_t; \\ COL1:COL2 \\ 1:2 \\ 2:2 \\ 3:3 \\ 4:3 \\ 5:4 \\ (5行)| |
同じSQLでも結果が変わる。
===== DUAL表の追加 =====
OracleではDUAL表があるが、PostgreSQLにない。\\
PostgreSQLはFROMを書く必要がない。
-- Oracle
SELECT '' FROM DUAL
-- PostgreSQL
SELECT ''
移行作業で困らないようにDUAL表を作成することで、Oracleと同じようになる。
CREATE TABLE DUAL (
DUMMY CHARACTER VARYING(1) DEFAULT '1' NOT NULL
)
WITH (
OIDS=FALSE
);
-- OWNER設定
ALTER TABLE DUAL
OWNER TO postgres;
TABLEを作成するよりVIEWにする方が一般的。\\
[[https://thinkit.co.jp/cert/marugoto/2/1/21/2.htm|PostgreSQLとOracleによるデータベース相互移行マニュアル - ThinkIT]]
CREATE VIEW dual AS
SELECT 'X' AS DUMMY;
===== SQL文の移行 =====
==== テーブル名、カラム名の大文字、小文字の区別 ====
>引用符が付かない名前は常に小文字に解釈されますが、識別子を引用符で囲むことによって大文字と小文字が区別されるようになります。例えば、識別子FOO、foo、”foo”はPostgreSQLによれば同じものとして解釈されますが、”Foo”と”FOO”は、これら3つとも、またお互いに違ったものとして解釈されます(PostgreSQLが引用符の付かない名前を小文字として解釈することは標準 SQLと互換性がありません。標準SQLでは引用符の付かない名前は大文字に解釈されるべきだとされています。したがって標準SQLによれば、fooは”FOO”と同じであるべきで、”foo”とは異なるはずなのです。もし移植可能なアプリケーションを書きたいならば、特定の名前は常に引用符で囲むか、あるいはまったく囲まないかのいずれかに統一することをお勧めします)。
>https://www.postgresql.jp/document/9.6/html/sql-syntax-lexical.html
テーブル名やカラム名を使用した抽出条件があった場合、PostgreSQLでは英小文字になるため英大文字を英子文字に変更する必要がある。
==== 結果セットにおける行番号の指定 ====
^Oracle^PostgreSQL^
| ROWNUM | OFFSET|
| LINE BETWEEN | OFFSET + LIMIT|
SELECT * FROM 商品マスタ
WHERE 卸単価 IS NOT NULL
AND ROWNUM <= 5
ORDER BY 卸単価 DESC;
SELECT * FROM 商品マスタ
WHERE 卸単価 IS NOT NULL
ORDER BY 卸単価 DESC
LIMIT 5 OFFSET 0;
==== 外部結合(アウタージョイン)構文 ====
^Oracle^PostgreSQL^
| WHERE 表1.列A(+) = 表2.列A | FROM 表1 RIGHT OUTER JOIN 表2 \\ ON(表1.列A = 表2.列A)|
| WHERE 表1.列A = 表2.列A(+) | FROM 表1 LEFT OUTER JOIN 表2 \\ ON(表1.列A = 表2.列A)|
==== 副問い合わせ ====
スカラ副問い合わせの場合は、OracleとPostgreSQLで構文は変わらない。\\
だだし、FROM句 or WHERE句内で使用する場合、PostgreSQLではエイリアスが必要となる。
* スカラ副問い合わせ
違いなし
SELECT 商品名 FROM 商品マスタ
WHERE 卸単価 =
(SELECT MAX(卸単価) FROM 商品マスタ);
* FROM句内での副問い合わせ
SELECT 担当者名
FROM (SELECT *
FROM 担当者マスタ
WHERE 生年月日< '1970-1-1')
WHERE MGR_ID IS NOT NULL;
SELECT 担当者名
FROM (SELECT *
FROM 担当者マスタ
WHERE 生年月日< '1970-1-1') T
WHERE MGR_ID IS NOT NULL;
==== DELETE文のFROM句にJOINが使えない ====
USING句やIN句に変更する。\\
[[https://kailo.jp/post/10|PostgreSQLでJOINした結果をDELETEする]]
==== MERGE文サポート(PostgreSQL 15以降) ====
PostgreSQL 15でMERGE文がサポートされました。
* [[https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/09/pg15_report_20220906_rev2.pdf|PostgreSQL15 検証レポート - pdf]]
* [[https://dev.classmethod.jp/articles/postgresql15-suppoerts-merge-syntax-upsert/|PostgreSQL 15にMERGE文UPSERTがやってくる]]
==== 集合演算 ====
UNION句、INTERSEC句での変更はない。ただし、OracleのMINUS句は、PostgreSQLではEXCEPT句に変更する必要がある。\\
※性能検証した際にEXCEPT句では遅かったため、NOT EXISTS 書き換えたことがあります。単純に変換すればいいわけではない。
SELECT 担当者ID, 担当者名
FROM 担当者マスタ
MINUS
SELECT 支店担当者ID, 支店担当者名
FROM 支店担当者マスタ
ORDER BY 1;
SELECT 担当者ID, 担当者名
FROM 担当者マスタ
EXCEPT
SELECT 支店担当者ID, 支店担当者名
FROM 支店担当者マスタ
ORDER BY 1;
==== 複合インデックスの指定方法 ====
インデックス定義の最初の列から連続する任意数の右方向の列を含む問い合わせに対して複数列インデックスを使用することができます。\\
https://www.postgresql.jp/document/8.0/html/indexes-multicolumn.html
複合インデックスは**最初の列から連続して列が指定されている場合にのみ機能する**という制限があるからです。\\
そのため、インデックス定義の途中の列から条件を指定するとインデックスが効かず遅くなる。
WHERE AC = xxx AND AD = xxx
↓
WHERE AA = xxx AND AB = xxx AND AC = xxx AND AD = xxx
=== bloomインデックスを使用する ===
主キーが複数の場合のインデックスにはデフォルトのb-treeインデックスが使用されるが、弱点として先頭のキーが外れるとインデックスが使われなくなる。その場合に複数列インデックスには、bloomインデックスを使用することで、先頭のキーが条件が外れていてもインデックスが使われる。\\
[[https://qiita.com/nuko_yokohama/items/4a7fe2a731a479189034|bloomインデックス]]
==== ダブルパイプ結合したインデックスは作成できない ====
PostgreSQLではカラムをダブルパイプ結合するとインデックスが効かなくなります。\\
Oracleではダブルパイプ結合したインデックス自体を作成できましたが、PostgreSQLではできません。\\
PostgreSQLでは単一または複合インデックスを作成し、行値式を使用することでインデックスを効くようにします。\\
SELECT * FROM TEST
WHERE
(UPDATE_DATE, UPDATE_TIME) > ('20190218','141435') AND
(UPDATE_DATE, UPDATE_TIME) <= ('20190219','141435')
※Oracleの行値式は等価(=,!=,IN)しか対応していないが、PostgreSQLでは範囲条件(>, <=など)も対応している。なお、SQLServerは未だに行値式をサポートしていない。
==== ユーザ定義関数 ====
=== NVL関数 ===
Oracle関数から移植する際に、NVL→coalesce(読み:こぅあれす)に変換するは大変なのでユーザー関数のNVLを作ることでSQL変更を回避する。
[[https://stackoverflow.com/questions/17203080/porting-oracle-nvl-to-postgres-coalesce-fails|Porting Oracle NVL to Postgres Coalesce fails]]
-- 型違いの複数登録が必要
CREATE OR REPLACE FUNCTION nvl(expr1 text, expr2 text)
RETURNS text AS
$BODY$
SELECT coalesce($1, $2);
$BODY$
LANGUAGE sql;
CREATE OR REPLACE FUNCTION nvl(expr1 date, expr2 date)
RETURNS date AS
$BODY$
SELECT coalesce($1, $2);
$BODY$
LANGUAGE sql;
CREATE OR REPLACE FUNCTION nvl(expr1 numeric, expr2 integer)
RETURNS numeric AS
$BODY$
SELECT coalesce($1, $2);
$BODY$
LANGUAGE sql;
CREATE OR REPLACE FUNCTION nvl(expr1 text, expr2 integer)
RETURNS integer AS
$BODY$
SELECT COALESCE(cast($1 as int), $2);
$BODY$
LANGUAGE SQL;
CREATE OR REPLACE FUNCTION nvl(expr1 NUMERIC, expr2 text)
RETURNS text AS
$BODY$
SELECT COALESCE($1||'', $2);
$BODY$
LANGUAGE SQL;
=== add_month関数 ===
Oracle関数から移植する際にユーザー関数のADD_MONTHSを作ることでSQL変更を回避する。
CREATE OR REPLACE FUNCTION ADD_MONTHS(TIMESTAMP with time zone,integer)
RETURNS TIMESTAMP with time zone AS
$BODY$
SELECT $1 + $2 * interval '1 month'
$BODY$
LANGUAGE 'sql'
=== 動的クエリ ===
「EXECUTE sql;」として、動的クエリを実行させる。また、「RETURN QUERY」としてテーブルを返す。\\
[[http://shin-hanuman.blogspot.jp/2011/06/postgresql.html|PostgreSQL めも - ストアドプロシージャ]]
CREATE FUNCTION func_FugaTable(key text)
RETURNS TABLE(col1 text, col2 text) AS $$
DECLARE
sql TEXT;
BEGIN
sql := 'SELECT id::text, name::text FROM t_Fuga WHERE id = ''' || key || '''';
RETURN QUERY EXECUTE sql;
END;
$$ LANGUAGE plpgsql;
==== トリガ関数 ====
* Oracleではトリガー単体ですんだが、PostgreSQLではトリガーとトリガー専用関数のセットとなっている。
* Oracle は IN、OUT、INOUT というパラメータを関数に渡すことができ、PostgreSQLは IN のみとなっている。 \\ [[https://www.postgresql.jp/document/8.0/html/plpgsql-porting.html|35.11. Oracle PL/SQLからの移植]]
* PostgreSQLではTRIGGERのREPLACEが存在しないため、更新の際はTRIGGERの削除が必要になる。
トリガ関数の作成と呼び出し方法について記す。
--Insert Beforeトリガ
CREATE OR REPLACE TRIGGER TBI_MSTW000010
BEFORE INSERT
ON MSTW012010
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
:new.CA := TO_CHAR(SYSDATE,'YYYYMMDD');
:new.CB := TO_CHAR(SYSDATE,'HH24MMSS');
END;
--Update Beforeトリガ
CREATE OR REPLACE TRIGGER TBU_MSTW000010
BEFORE UPDATE
ON MSTW000010
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
:new.CC := TO_CHAR(SYSDATE,'YYYYMMDD');
:new.CD := TO_CHAR(SYSDATE,'HHMMSS');
END;
PostgreSQLでは、トリガー専用の関数(引数なし)を別途作成する必要がある。
-- トリガ関数
CREATE OR REPLACE FUNCTION db_test."MSTW000010_insert"() RETURNS trigger AS
$BODY$BEGIN
NEW.CA := to_char(current_timestamp,'YYYYMMDD');
NEW.CB := to_char(current_timestamp,'HH24MISS');
RETURN NEW;
END;
BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION db_test."MSTW000010_insert"()
OWNER TO postgres;
CREATE OR REPLACE FUNCTION db_test."MSTW000010_update"() RETURNS trigger AS
$BODY$BEGIN
NEW.CC := to_char(current_timestamp,'YYYYMMDD');
NEW.CD := to_char(current_timestamp,'HHMISS');
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION db_test."MSTW000010_update"()
OWNER TO postgres;
--- Insert Before トリガ
CREATE TRIGGER "TBI_MSTW000010"
BEFORE INSERT
ON db_test.mstw000010
FOR EACH ROW
EXECUTE PROCEDURE db_test."MSTW000010_insert"();
--- Update Before トリガ
CREATE TRIGGER "TBU_MSTW000010"
BEFORE UPDATE
ON db_test.mstw000010
FOR EACH ROW
EXECUTE PROCEDURE db_test."MSTW000010_update"();
==== 自律型トランザクション ====
自律型トランザクションというのは、実行中のメイン・トランザクションによって開始される独立(自律)したトランザクションのことです。メイン・トランザクションと自律型トランザクションのコミット・ロールバック操作は互いに影響を及ぼさず、完全に独立した別個のトランザクションとなります。
Oracleは自律型トランザクションを使用できるが、PostgreSQLでは使用できない。
=== 対応 ===
dblinkを使用して自律型トランザクションを実現させる。\\
[[https://qiita.com/yaju/items/21434db1c08a9fef473a|【PostgreSQL】SELECT内で他のテーブルを更新する]]
===== OracleからPostgresSQL移行ツール =====
==== 移行支援するツール(ora2pg) ====
* [[https://ja.osdn.net/projects/sfnet_ora2pg/|ora2pg]]
* [[https://www.ntt-tx.co.jp/column/postgresql_blog/20171027/|このora2pgがすごい!Oracleのストアドプロシージャ2種をPostgreSQLに移行してみた!]]
* [[https://stackoverflow.com/questions/47345373/how-to-install-ora2pg-on-windows-7|how to install ora2pg on windows 7?]]
* [[http://blue-red.ddo.jp/~ao/wiki/wiki.cgi?page=Oracle%A4%AB%A4%E9Postgresql%A4%D8%B0%DC%B9%D4%A4%B9%A4%EB|OracleからPostgresqlへ移行する]]
* [[https://ipsj.ixsq.nii.ac.jp/ej/index.php?action=pages_view_main&active_action=repository_action_common_download&item_id=90486&item_no=1&attribute_id=1&file_no=1&page_id=13&block_id=8|オープンソースミドルウェアへの移行のための 移植開発支援ツールの評価 pdf]]
ora2pgを動作させるには、Perl5が必要
* [[http://vogel.at.webry.info/201503/article_1.html|Windows で使用できるフリーの Perl実行環境 Strawberry Perl]]
==== Oracle互換関数ライブラリ(oraface) ====
[[http://orafce.github.io/orafce/index-ja.html|Orafce ホームページへようこそ]]
==== db_syntaxdiff ====
NTTが開発しているオープンソースのツールである。Ora2Pgが対応していないSELECT文やUPDATE文などのデータ操作を行うSQLに対応している。プログラム言語の中に文字列リテラルとして埋め込まれたSQLコードに対しても利用できる。ただし、実際の修正そのものは人手によって行う必要がある。\\
https://github.com/db-syntax-diff
* APをPostgreSQLへ移植する際の影響箇所を検出するツール
* 影響箇所抽出作業の短縮化
* 抽出の網羅性向上、品質の均⼀化
==== EDB Postgres(Oracle互換性の高いPostgreSQL) ====
EDB PostgresはOracle Databaseとの高い互換性を持っており、Oracle Databaseと同じSQL構文、ファンクション、プロシージャをサポートしています。旧製品名Postgres Plus Advanced Server\\
[[https://www.ashisuto.co.jp/product/category/database/postgresql/detail/list/oracle.html|EDB Postgres(PostgreSQL) 製品情報]]
^Oracleで使用しているSQL^PostgreSQLでの対応^EDB Postgresでの対応^
|・外部結合演算子(+)|・外部結合OUTER JOIN|対応|
|・日付、時刻 日付関数や書式|・ほぼすべて変更が必要|一部互換動作に対応|
|・条件分岐関数 nvlなど| |対応|
|・トランザクション内のROLLBACK挙動|・対応不可|Oracle互換モードを選択可能|
|・FROM句内のサブクエリで別名不要|・FROM句内サブクエリの別名必須|対応|
|・集合演算子 minus|・集合演算子 except|対応|
|・NULLと空文字の区別がない|・対応不可|一部互換動作を設定可能|
|・ROWNUM \\ ・MERGE文| |・oidまたはROWNUMで対応(制限あり) \\ ・INSERT ・・・ON CONFRICT|
以下の互換ユーティリティが提供されています。
^Oracle Database (コマンド)^Postgres Plus (コマンド)^
|SQL*Plus (sqlplus)|EDB*Plus (edbplus.sh)|
|SQL*Loader (sqlldr)|EDB*Loader (edbldr)|
|Wrap (wrap)|EDB*Wrap (edbwrap)|
* [[https://www.slideshare.net/InsightTechnology/b22-postgres-plus-advanced-servershinoda|PostgresPlus Advanced Server の Oracle Database 互換機能検証 - SlideShare]]
* [[https://www.ashisuto.co.jp/corporate/column/technical-column/detail/1198517_2274.html|Vol.3 Postgres Plus Advanced Server(体験編1)]]
* [[https://www.ashisuto.co.jp/corporate/column/technical-column/detail/1198518_2274.html|Vol.4 Postgres Plus Advanced Server(体験編2)]]
* [[https://www.slideshare.net/kkida85/dbpostgresql|商用DBからPostgreSQLへ まず知っておいて欲しいまとめ - SlideShare]]
* [[https://razokulover.hateblo.jp/entry/2019/06/19/193917|Oratopostgres-hiroshima - SlideShare]]
===== 参照 =====
* [[http://d.hatena.ne.jp/taramonera/20110121/1295574794|PostgreSQL select で文字列を連結するときの注意点]]
* [[http://dice-k-0.blogspot.jp/2011/11/postgresql.html|PostgreSQLで文字列の結合(||)をすると勝手にトリムされる(固定長文字列を生成する際には注意)]]
* [[https://blog.kawa-xxx.jp/entry/20140609/1402318085|PostgreSQLで to_char()すると前に半角スペースが入る]]
* [[http://thinkit.co.jp/free/marugoto/2/1/22/1.html|PostgreSQLとOracleによるデータベース相互移行マニュアル]]
* [[http://www.postgresql.jp/document/7.3/programmer/plpgsql-porting.html|Oracle PL/SQL からの移植]]
* [[http://infra-engineer.com/database/oracledb-postgresql/|Oracle DatabaseとPostgreSQLの違い]]
* [[http://www.confrage.com/database/postgresql/ora_pos/diff_ora_pos/diff_ora_pos.html|OracleとPostgreSQLの違い PostgreSQLでは、nullと空文字は区別されます。]]
* [[http://heppoen.seesaa.net/article/148020207.html|[Oracle][PostgreSQL]Date型の違い]]
* [[http://www.office-iwata.com/archives/49|Oracle PL/SQL から PostgreSQL PL/pgSQL への移植の注意メモ PostgreSQL では関数をオーバーロードすることができます。]]
* [[http://d.hatena.ne.jp/zankey/20070914/postgresql|PostgreSQL の SELECT句で Null文字列を連結する方法]]
* [[http://www.ne.jp/asahi/hishidama/home/tech/postgres/sql.html|PostgreSQLのSQL]]
* [[http://log.miraoto.com/2012/03/358/|PostgreSQLでカラムを追加する際にdefault値を設定する方法]]
* [[http://d.hatena.ne.jp/sternheller/20090717/1247853538|PostgreSQLで自動的にインデックスが生成される条件]]
* [[http://qa.atmarkit.co.jp/q/2395|PostgreSQLとMySQLはどちらかに明確な優位性がありますか、というの質問・回答]]
* [[https://shobon.hatenablog.com/entry/2014/03/16/142256|テーブル名やカラム名にSQLの予約語を使う方法]]
* [[https://kaede.jp/2015/06/20210652.html|Oracle⇔Postgresqlの組み込みデータ型対応表]]
* [[http://www.meibinlab.jp/nishijima/archives/135|WHERE句でエイリアスを使う]]