一、前提
首先須要對Oracle和PostgreSQL的SQL都比較熟悉。對其理解的越詳細就越具備優點,本文幫助讀者迅速理解這兩類SQL的區別是什麼。
若是因ACS/pg而須要將Oracle移植到PG,那麼就須要熟悉AOLserver Tcl,尤爲是SOLserver的API。本文,主要討論:
Oracle 10g到11g(大多數能夠適用到8i)
Oracle 12c某些方面會有不一樣,可是遷移更加便捷
PostgreSQL 8.4,甚至適用更早版本。
二、事務
Oracle這個數據庫會使用事務,那麼PostgreSQL也須要激活事務。多個DML語句組成一個代碼片斷,而這些語句不會當即提交,那麼就須要使用BEGIN語句開啓一個事務,而後將這些語句包含在BEGIN這個塊中。Oracle和PG中ROLLBACK和COMMIT、SAVEPOINT的語義相同。Oracle的隔離級別,PostgreSQL中也有。大多數狀況下PG的隔離級別(讀已提交)就已知足需求。
三、語法差別
PG中有少數語法不一樣但功能相同SQL。ACS/pg會自動進行轉換,只有大部分函數不一樣,須要手工進行轉換。這個工做由db_sql_prep來完成。
函數
Oracle有超過250個內置單行函數和不止50個聚合函數,詳情查看:https://wiki.postgresql.org/wiki/Oracle_Functions。
Sysdate
Oracle使用sysdate函數獲取當前日期和時間(以服務器的時區爲準)。Postgres使用’now’::timestamp做爲當前事務啓動的日期和時間。ACS/pg將這個包裝成sysdate()函數。
ACS/pg還包括Tcl過程,即db_sysdate。所以:
set now [database_to_tcl_string $db "select sysdate from dual"]
應該變成:
set now [database_to_tcl_string $db "select [db_sysdate] from dual"]
Dual表
Oracle的SELECT中實際不須要表名的地方可使用表DUAL,由於Oracle中的FROM子句是必須的。Postgsql中能夠將FROM子句丟棄。能夠在postgres中建立一個視圖做爲這個表從而消除上述問題。這樣就能夠在不干擾Postgres的解析器狀況下兼容Oracle的SQL。遷移過程當中,儘量去掉「FROM DUAL」子句。由於和jual進行join比較奇怪。
ROWNUM和ROWID
Oracle的虛擬列ROWNUM:在執行ORDER BY前讀取數據時分配一個數值。不少場景下可使用ROW_NUMBER() OVER(ORDER BY...)替代。可是使用序列進行模擬時可能會使性能慢些。
Oracle的虛擬列ROWID:錶行的物理地址,以base64編碼。應用中可使用該列臨時緩存行地址,使第二次訪問時更加便捷。Postgres的ctid起一樣的做用。
序列
Oracle的序列語法是sequence_name.nextval。
Postgres的序列語法是nextval('sequence_name')。
Tcl中,獲取寫一個序列值能夠抽象爲調用[db_sequence_nextval $db sequence_name]。若是須要在一個複雜的SQL語句中使用序列值,可使用 [db_sequence_nextval_sql sequence_name]。
解碼
Oracle的解碼函數使用方法:decode(expr, search, result [, search, result...] [, default])
爲了評估這個表達式,Oracle一個一個地比較expr和search值。若是expr等於search,Oracle返回對應的result。若是沒有找到匹配值,返回default或者null。
Postgres沒有這樣的結構,可是可使用下面格式替代:
CASE WHEN expr THEN expr [...] ELSE expr END
例如:CASE WHEN c1 = 1 THEN 'match' ELSE 'no match' END,返回第一個爲真的謂詞對應的表達式。
DECODE和CASE的模擬方式有一點不一樣:DECODE (x,NULL,'null','else'),若是x爲NULL則返回NULL;而CASE x WHEN NULL THEN 'null' ELSE 'else' END,則返回’else’的result。Oracle一樣。
NVL
Oracle還有其餘便捷函數:NVL。若是不爲NULL,NVL返回第一個參數,不然返回第二個參數:start_date := NVL(hire_date, SYSDATE);。若是hire_date爲NULL,則前面的語句會返回SYSDATE。Postgres和Oracle有一個函數以更廣泛的方式執行一樣的行爲: coalesce(expr1, expr2, expr3,....),返回第一個非NULL表達式。
FROM中子查詢
Postgresql中子查詢須要使用括號包含,並提供一個別名。Oracle中不須要別名:
Oracle: SELECT FROM (SELECT FROM table_a)
Postgresql: SELECT FROM (SELECT FROM table_a) AS foo
四、功能差別
Postgresql並不具有Oracle全部功能。ACS/pg經過指定的方案解決這些限制。雖然postgres具有大部分功能,可是一些特性還須要等待其新版本發佈。
Outer joins
Oracle老版本9i以前,outer join:
SELECT a.field1, b.field2
FROM a, b
WHERE a.item_id = b.item_id(+)
(+)表示,若是表b中沒有匹配的item_id值,匹配會繼續下去,會做爲一個空行進行匹配。Postgresql和Oracle 9i及以前版本:
SELECT a.field1, b.field2
FROM a
LEFT OUTER JOIN b
ON a.item_id = b.item_id;
只有匯聚值從outer joined表中提取時,也可能不使用join。若是原始查詢:
SELECT a.field1, sum (b.field2)
FROM a, b
WHERE a.item_id = b.item_id (+)
GROUP BY a.field1
Postgres的查詢:SELECT a.field1, b_sum_field2_by_item_id (a.item_id) FROM a,此時能夠定義函數:
CREATE FUNCTION b_sum_field2_by_item_id (integer)
RETURNS integer
AS '
DECLARE
v_item_id alias for $1;
BEGIN
RETURN sum(field2) FROM b WHERE item_id = v_item_id;
END;
' language 'plpgsql';
Oracle 9i開始將支持SQL 99的 outer join語法。可是一些程序員仍然使用舊語法,因此這篇文章顯得有意義。
CONNECT BY
Postgres不支持connect by語句。可使用WITH RECURSIVE替代。因爲WITH RECURSIVE是圖靈完畢的,所以很容易將CONNECT BY語句轉換成WITH RECURSIVE。有時還能夠將CONNECT BY當作一個簡單的iterator:
SELECT ... FROM DUAL CONNECT BY rownum <=10
等價於:
SELECT ... FROM generate_series(...)
NO_DATA_FOUND and TOO_MANY_ROWS
默認狀況下PL/pgsql禁止使用此異常。當須要在存儲的PLpgSQL代碼中進行單行檢查時,須要在全部SELECT中的任何關鍵字INTO以後添加關鍵字STRICT。
五、數據類型
Postgres嚴格尊周SQL表中,而Oracle因爲歷史緣由,會有本身特有的方式,尤爲是數據類型方面。
空字符串與NULL
Oracle中,strings()空和NULL在字符串內容中相同。能夠將NULL和和一個字符串鏈接起來做爲結果。可是在postgres中,這種狀況獲得的結果是NULL。Oracle中須要使用IS NULL操做符來檢測字符串是否爲空。Postgres中,對於空字符串獲得的結果是FALSE,而NULL獲得的是TRUE。當從Oracle向postgres轉換時,須要分析字符代碼,分離出NULL和空字符串。
Numeric類型
Oracle中常用NUMBER數據類型,PG中對應的數據類型時DECIMAL或者NUMERIC。PG中的numbers限制(小數點前到131072位,小數點後16383位)比Oracle高,內部存儲方式相同。Oracle的FLOAT在PG中是REAL,DOUBLE是DOUBLE PRECISION。
Date and Time
Oracle中的DATE包含data和time。不少中狀況下,使用PG中的TIMESTAMP就足夠了。因爲date只包含秒、分、小時、天、月和年,因此一些狀況下不是精確的結果。沒有幾分鐘、沒有夏令時、沒有時區。Oracle的TIMESTAMP和PG相似。
Oracle只有INTERVAL YEAR TO MONTH and INTERVAL DAY TO SECOND,所以PG能夠直接使用。
CLOBs
PG以TEXT的形式對CLOB有不錯的支持。
BLOBs
PG對二進制大對象支持很是差。由於不能使用pg_dump進行dump因此不適合在24/7環境中使用。利用大對象的數據庫進行備份時,須要將數據庫關閉,而後直接備份數據目錄。
Don Baccus修改了SOLserver的PG驅動,經過編碼/解碼二進制文件,從而支持二進制大對象。數據庫在運行時進行dump,這些結果對象能夠用來保證一致性,從而在備份時不須要中斷服務。
爲了繞過PG對元組大小對於一個塊的限制,驅動程序將編碼的數據分紅8K大小的塊。PG將在2000年夏天對大對象進行大修。所以,只實現了ACS使用的BLOB功能。
爲了使用BLOB驅動擴展,首先須要建立一個表,其lob列定義爲interger類型,再建立一個觸發器on_lob_ref。例如:
create table my_table (
my_key integer primary key,
lob integer references lobs,
my_other_data some_type -- etc
);
建立一個觸發器my_table_lob_trig,在insert或delete或update前觸發:
set lob [database_to_tcl_string $db "select empty_lob()"]程序員
ns_db dml $db "begin"
ns_db dml $db "update my_table set lob = $lob where my_key = $my_key"
ns_pg blob_dml_file $db $lob $tmp_filename
ns_db dml $db "end"sql
主要,調用時需將其包裝在一個事務中,即便此時沒有進行update。:
set lob [database_to_tcl_string $db "select lob from my_table
where my_key = $my_key"]
ns_pg blob_write $db $lob數據庫
六、其餘工具
Ispirer MnMTK:自動遷移整個數據庫schema並將Oracle數據轉換成PG的數據的工具集。
Full Convert:將Oracle轉換成PG,每秒100K個記錄。
Oracle to Postgres data migration and sync:每4-5分鐘轉換1M個記錄。基於觸發器的數據庫同步方法和並行雙向同步方式可幫助輕鬆地管理數據。
ESF Database Migration Toolkit:直連Oracle和PG,遷移表結構、數據、索引、主鍵、外鍵、內容等。
Orafce:兼容Oracle的函數。好比date函數(next_day,last_day,trunc,round等)、字符串函數、一些包DBMS_ALERT, DBMS_OUTPUT, UTL_FILE, DBMS_PIPE等。
Ora2pg:Perl腳本,兼容schema。鏈接Oracle,提取結構,產生SQL語句而後加載到PG。
Oracle to postgres:不使用ODBC和其餘中間件。轉換表結構、數據、索引、主鍵和外鍵。
ora_migrator:PL/pgSQL擴展,充分利用Oracle的Foreign Data Wrapper。
七、原文
https://wiki.postgresql.org/wiki/Oracle_to_Postgres_Conversion緩存