一.PostgreSQL數據庫簡介:
首先先百度一輪簡介,PostgreSQL 是最先進的開放源碼的數據庫系統, 它提供了多版本並行控制,支持幾乎所有 SQL 構件(包括子查詢,事務和用戶定 義類型和函數), 並且可以獲得非常廣闊範圍的(開發)語言綁定 (包括 C,C++,Java,perl,tcl,和 python)。由於PostgreSQL的開源免費,經歷了從Oracle遷移PG數據庫,遇到了各種坑,閒下來總結下,可能不是很完善,以後工作中遇到問題再來補充。
二.Oracle與PostgreSQL語法對比:
序號 | 類型 | Oracle | PostgreSQL |
---|---|---|---|
1 | 當前時間 | SYSDATE | 可全部使用current_timestamp替換 |
2 | 序列 | SEQNAME.NEXTVAL | NEXTVAL('SEQNAME') |
3 | 固定值列 | SELECT '1' AS COL1 | SELECT CAST('1' AS TEXT) AS COL1 |
4 | NVL | NVL函數 | NVL可以用COALESCE函數替換 |
5 | 類型自動轉換 | Oracle某些情況下支持類型自動轉換 | 會出現類型不匹配等錯誤,需要在Java或者sql中進行類型轉換,使類型匹配 |
6 | INSTR函數 | instr('str1','str2') | strpos('str1','str2') |
7 | 外連接 | Oracle可簡寫爲(+) | 用LEFT JOIN等語句替換 |
8 | 層次查詢 |
START WITH語句; CONNECT BY語句; |
用WITH RECURSIVE語句; |
9 | 數據庫對象大小寫 | 不區分大小寫 | 創建數據庫對象時要小寫,這樣纔不區分SQL的大小寫 |
10 | DECODE等判斷函數 | DECODE() | 用標準的CASE WHEN THEN ELSE END語句替換 |
11 | 同義詞 | Oracle支持同義詞 | 用視圖代替 |
12 | DUAL | SELECT 1+1 FROM DUAL | SELECT 1+1或者CREATE VIEW dual AS SELECT current_timestamp |
13 | ROWNUM | ROWNUM關鍵字 |
兩種情況: 1.限制結果集數量,用於翻頁等:SELECT * FROM T LIMIT 5 OFFSET 0; 2.生成行號:使用分析函數ROW_NUMBER() OVER(); |
14 | TO_CHAR | TO_CHAR(COL,FMT),格式化字符串可以爲空,用於類型轉換 |
TO_CHAR(COL1,FMT),不支持FMT爲空,使用類型轉換使用cast()或者string::text的方式,詳細定義見:http://www.postgresql.org/docs/9.4/static/functions-formatting.html |
15 | TO_NUMBER | TO_NUMBER(COL,FMT),格式化字符串可以爲空 |
TO_NUMBER(COL1,FMT),不支持FMT爲空,使用類型轉換使用cast()或者string::text的方式,詳細定義見:http://www.postgresql.org/docs/9.4/static/functions-formatting.html |
16 | NULL和'' | ORACLE認爲''等同於NULL | NULL和''不同 |
17 | NULL和'' | LENGTH('')爲NULL | LENGTH('')爲0 |
18 | NULL和'' | TO_DATE('','YYYYMMDD')爲空 | TO_DATE('','YYYYMMDD')爲0001-01-01 BC |
19 | NULL和'' | TO_NUMBER('',1)爲NULL | TO_NUMBER('',1),報錯 |
20 | NULL和'' |
INSERT INTO TEST(VALUE4)VALUES(''); [Result]VALUE4=NULL (注:VALUE4字段爲數值類型) |
INSERT INTO TEST(VALUE4)VALUES(''); VALUE4=NULL |
21 | NULL和'' |
INSERT INTO TEST(VALUE4)VALUES(''); [Result]VALUE4=NULL (注:VALUE4字段爲字符類型) |
INSERT INTO TEST(VALUE4)VALUES(''); VALUE4='' |
22 | NULL和'' |
INSERT INTO TEST(VALUE4)VALUES(TO_DATE('','YYYYMMDD')); [Result]VALUE4=NULL (注:VALUE4字段爲時間類型) |
INSERT INTO TEST(VALUE4)VALUES(TO_DATE('','YYYYMMDD')); [Result]VALUE4=0001-01-01 BC |
23 | SUBSTR函數 | 如果從第一個開始取子串,可以從0開始,也可以從1開始,如果不是第一個開始,則從1開始計數,可以爲負值,從字符串結尾計數,用於取最後幾位。 | 從1開始計數。如果要取最後幾位,可以用RIGHT函數解決。 |
24 | 子查詢別名 | 如果FROM後只有一個子查詢,該子查詢可以沒有別名 | 必須有別名 |
25 | 列(別)名爲關鍵字 | Oracle中比如name,type這樣的關鍵字可以直接作爲列的別名,比如:select xx name from t | 需要加as,比如select xx as name from t |
26 | 當前登錄用戶 | SELECT USER FROM DUAL | select current_user |
27 | 修改表字段類型 |
1.如果字段無數據,可直接修改; 2.如果有數據且新類型和原類型兼容,也可以直接修改; 3.如果不兼容,可通過對原字段改名,然後增加新字段,再通過UPDATE語句對數據進行處理; |
1.如果新類型和原類型兼容,可直接修改; 2.如果不兼容,需要使用USING關鍵字然後提供一個類型轉換的表達式; |
28 |
表名 |
默認爲大寫 |
默認爲小寫 |
29 |
事務處理機制差異 |
oracle後續語句可以正常操作。 |
Pg事務中任何操作失敗都會導致後續語句的失敗(包括查詢,dml,lock[ nowait]等),可以開啓ON_ERROR_ROLLBACK臨時解決(影響性能) |
30 |
DDL |
Oracle執行 DDL語句如CREATE, DROP, RENAME, or ALTER時,會隱式提交事務; |
PG在執行這類語句時,不會提交事務,需顯式提交。 |
31 |
字符類型 |
GBK:漢字字符2個字節,ASCII碼1個字節 |
UTF8字符集,漢字3個字節,ASCII嗎1個字節 |
32 | 查看數據庫中某個表的索引 | select * from user_indexes where table_name=upper('表名') | select * from pg_statio_all_indexes where relname='表名' |
33 | 查看錶所佔磁盤空間大小 | select * from (select owner TABLESPACE_NAME,SUM(BYTES/1024/1024/1024),SEGMENT_NAME FROM dba_segments where segment_type like 'TABLE%' GROUP BY SEGMENT_NAME,TABLESPACE_NAME,owner order by 3 desc) where rownum<=10; | 1.PG數據庫查看所有表所佔磁盤空間大小
SELECT SUM(t.SIZE/1024/1024/1024) from ( select table_schema || '.' || table_name as table_full_name,pg_total_relation_size('"'|| table_schema || '"."'||table_name|| '"') as size 2.PG數據庫查看每個表所佔磁盤空間大小 |
34 | 查詢創建索引的語句 | select dbms_metadata.get_ddl ('INDEX','PK_TBL_ACC_INFO') from dual; | select * from pg_indexes where tablename='表名' |
三.Oracle與PostgreSQL差異導致的一些坑:
1.性能測試,通過性能測試發現慢SQL瓶頸,由於oracle和pg有着不同的SQL優化器,所以同一個SQL兩邊可能走不同的執行計劃,進而產生性能差異。
2.postgreSQL 中的||用法與其他數據庫不同:
select a||b from table1;當a或b其中一個爲null時,該查詢返回null。如果不希望要這個結果,可以使用COALESCE函數:
select COALESCE(a,'')||COALESCE(b,'') from table1;
3.postgreSQL 一個類型轉換聲明一個從一種數據類型到另外一種數據類型的轉換。
CAST ( expression AS type ) expression::type
CAST語法遵循 SQL 標準;::語法是PostgreSQL歷史用法。
4.rownum
PostgresQL 中沒有rownum ,無法 使用where rownum < = X 的方法進行分頁,取而代之的是limit X ,offset Y 方法, 而ORACLE 中不允許使用LIMIT X 的 方法
ORACLE:
SELECT * FROM ( SELECT * FROM (SELECT * FROM SCHEMA.PREFIX_TABLE1 ORDER BY COL1 DESC,COL2 ASC) where ROWNUM <= 50 ORDER BY COL3 ASC,COL4 DESC)
WHERE ROWNUM <= 20 ORDER BY COL5 DESC,COL6 ASC;
postgreSQL:
select * from ( select * from (SELECT * FROM SCHEMA.PREFIX_TABLE1 ORDER BY COL1 DESC,COL2 ASC) selb order by col3 asc,col4 desc limit 50 ) sela
order by col5 desc,col6 asc limit 20;
-- 注意!!limit 必須用於order by 之後
OFFSET說明在開始返回行之前忽略多少行。 OFFSET 0和省略OFFSET子句是一樣的。 如果OFFSET和LIMIT都出現了, 那麼在計算LIMIT個行之前忽略OFFSET行。
如果使用LIMIT,那麼用ORDER BY 子句把結果行約束成一個唯一的順序是一個好主意。 否則你就會拿到一個不可預料的該查詢的行的子集。 你要的可能是第十到二十行,但以什麼順序的十到二十? 除非你聲明瞭ORDER BY,否則順序是不知道的。
查詢優化器在生成查詢規劃的時候會考慮LIMIT,因此如果你給 LIMIT和OFFSET不同的東西,那麼你很可能收到不同的規劃(產生不同的行順序)。 因此,使用不同的LIMIT/OFFSET值選擇不同的查詢結果的子集將生成不一致的結果, 除非你用ORDER BY強制一個可預料的順序。這可不是臭蟲; 這是一個很自然的結果,因爲 SQL 沒有許諾把查詢的結果按照任何特定的順序發出,除非用了ORDER BY來約束順序。
OFFSET 子句忽略的行仍然需要在服務器內部計算;因此,一個很大的 OFFSET 可能還是不夠有效率的。
5.空串‘’和null:
在Oracle數據庫中空串和null是等同的,而在PG數據庫中兩者是不同的,這就會引發一個問題,以前Oracle程序中更新一個字段爲空串,但實際查詢時使用is null仍然是可以查到的;而在PG中是查不到的;解決方法有三種:
(1)修改更新代碼,但是工作量很大;
(2)修改後續查詢代碼,在查詢條件中加上 select * from A where (A.aa is null or A.aa =‘’);或者select * from A where (A.aa is not null and A.aa !=‘’),這樣工作量也不小;
(3)使用觸發器,當更新爲空串時,將其更新爲null;但是使用觸發器可能會有失效;
注意:如果表結構中存在not null 約束,代碼中更新爲空串時,依然可以生效,數據庫中會保存空串,因爲PG數據庫認爲空串和null是不同的,這是最坑的。
6.numeric問題:
在Oracle中,numeric類型如果小數點後有0,則會去掉小數點後的0;而PG數據庫會補0;比如numeric(10,3),在Oracle中如果是2.5,則會保存2.5;在PG數據庫中會保存爲2.500;
四.其他常用:
1.查看所有連接的用戶
select * from pg_stat_activity where state='idle';
2.結束連接的進程
--pg_cancel_backend只是取消當前某一個進程的查詢操作,但不能釋放數據庫連接;
--pg_terminate_backend可以在PG的後臺殺死這個進程;
select pg_terminate_backend(2501);--括號中pid
3.使用ASCII查看數據庫中記錄含有回車換行符的數據
select req_remark from accept_due_pay where req_remark like '%' ||chr(13)||'%' or req_remark like '%' ||chr(10)||'%'
4.去掉數據庫中回車換行符
select replace(replace(req_remark,chr(13),''),chr(10),'') from accept_due_pay where id in ('266363','266364','26365',)
5.PG數據庫修改關鍵字的字段名稱
alter table ent_bank_buss rename "CURRENT_DATE" to current_date1;
alter table ent_bank_buss rename "CURRENT_TIME" to current_time1;