[筆記]將系統的數據庫從MySQL 5.5遷移到PostgreSQL 9.1

環境
Windows Server 2003 x64 簡體中文, MySQL 5.5 (UTF8編碼), PostgreSQL 9.1.4-1 (UTF8編碼)
Spring 3.0.7, Struts 2.3.4, Hibernate 3.5.5 java

從MySQL遷移到PostgreSQL sql

-----------------------------分隔線-----------------------------
* 分頁寫法的區別
安全


PostgreSQL
MySQL
LIMIT 數量
支持
支持
LIMIT 下標, 數量
不支持
支持
LIMIT 數量 OFFSET 下標
支持
支持

e.g.
SELECT * FROM user LIMIT 10; -- PostgreSQL與MySQL均支持

SELECT * FROM user LIMIT 10, 10; -- PostgreSQL不支持,MySQL支持

SELECT * FROM user LIMIT 10 OFFSET 10; -- PostgreSQL與MySQL均支持
-----------------------------分隔線-----------------------------

* DDL定義中的各類寫法區別
** 前者可用符號"`"(不含引號,在鍵盤上是Esc鍵下面的那個鍵對應的字符)來包裹住表名、列名等,後者不能夠。
 
* 轉義字符
MySQL            \
PostgreSQL    默認不支持慣用的\,而是用的英文單引號'
 
* 使用DB
MySQL中能夠 use DB名;
PostgreSQL中不支持 use 關鍵字!

* 主鍵約束
兩者沒什麼區別
 
* 自增加主鍵
MySQL中寫成這樣
CREATE TABLE users (     id INT(11) NOT NULL AUTO_INCREMENT,     name VARCHAR(50) NOT NULL,     PRIMARY KEY (id) );
 
PostgreSQL中寫成這樣
CREATE TABLE users(     id serial NOT NULL,     name VARCHAR(50) NOT NULL,     PRIMARY KEY (id ) );
注: PostgreSQL會對 serial數據類型的列隱式生成名爲 表名_PK名_seq 的SEQUENCE,此例的SEQUENCE名爲 users_id_seq 。
 
* 改變已有表的自增加序列
-- 更安全有效的解決方案請參考http://stackoverflow.com/questions/244243/how-to-reset-postgres-primary-key-sequence-when-it-falls-out-of-sync/* 假若有表a從MySQL遷移過來,其中有數據100條,若在創建此表時沒有指定序列,PG會默認給此表的主鍵列一個
sequence——它是增加的,幅度爲1。此時若用Hibernate來給表a添加數據會報錯,說主鍵1已經存在!
因此能夠在遷移表以後,將表的sequence做少量修改,讓其從當前表的主鍵的最大值再加1來開始!便可解決
Hibernate添加數據時報錯的問題

ALTER SEQUENCE "public"."表名_主鍵名_seq" RESTART WITH (PK的最大值 + 1);
或
ALTER SEQUENCE 表名_主鍵名_seq RESTART WITH (PK的最大值 + 1);

e.g.
ALTER SEQUENCE file_types_id_seq" RESTART WITH 10;

從上面stackoverflow.com網站上獲得的更加簡單有效的一句SQL語句以下:
SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)+1);

*/

 * 惟一鍵約束
MySQL            UNIQUE KEY name (name)
PostgreSQL     UNIQUE  (name)

* 內置SQL函數區別
MySQL格式化日期            DATE_FORMAT(CURRENT_TIMESTAMP,'%Y-%m-%d %H:%i:%s')
PostgreSQL格式化日期     to_char(CURRENT_TIMESTAMP,'yyyy-mm-dd hh24:mi:ss'), to_date(text, text), to_timestamp(text, text)

* 日期類型
MySQL            date    time    datetime
PostgreSQL    timestamp

* 布爾類型
MySQL            木有,可用int(1)、枚舉或者字符串的方式來模擬。
PostgreSQL    boolean

* 外鍵約束
在MySQL中建立帶有外鍵的表寫法以下:  
DROP TABLE IF EXISTS recipient_recipientgroup; 

CREATE TABLE IF NOT EXISTS recipient_recipientgroup ( 
  id serial NOT NULL, 
  recipient_id INTEGER DEFAULT NULL, 
  recipient_group_id INTEGER DEFAULT NULL, 
  PRIMARY KEY (id), 
  KEY FK_recipient_recipientgroup_recipient (recipient_id), 
  KEY FK_recipient_recipientgroup_recipient_group (recipient_group_id), 
  CONSTRAINT FK_recipient_recipientgroup_recipient FOREIGN KEY (recipient_id) REFERENCES recipient (id), 
  CONSTRAINT FK_recipient_recipientgroup_recipient_group FOREIGN KEY (recipient_group_id) REFERENCES recipient_group (id) 
);
在 PostgreSQL 中建立帶有外鍵的表寫法以下: (即在MySQL的寫法上去掉 KEY ... 這句!)
DROP TABLE IF EXISTS recipient_recipientgroup; 

CREATE TABLE IF NOT EXISTS recipient_recipientgroup ( 
  id serial NOT NULL, 
  recipient_id INTEGER DEFAULT NULL, 
  recipient_group_id INTEGER DEFAULT NULL, 
  PRIMARY KEY (id), 
--  KEY FK_recipient_recipientgroup_recipient (recipient_id), 
--  KEY FK_recipient_recipientgroup_recipient_group (recipient_group_id), 
  CONSTRAINT FK_recipient_recipientgroup_recipient FOREIGN KEY (recipient_id) REFERENCES recipient (id), 
  CONSTRAINT FK_recipient_recipientgroup_recipient_group FOREIGN KEY (recipient_group_id) REFERENCES recipient_group (id) 
);
 
代碼修改
----------
* 將SpringSide 3.3.4中提供的IdEntity類修改以下
/**
 * 參考SpringSide3,統必定義id的entity基類.
 * 
 * 基類統必定義id的屬性名稱、數據類型、列名映射及生成策略.
 * 子類可重載getId()函數重定義id的列名映射和生成策略.
 */
//JPA 基類的標識
@MappedSuperclass
public abstract class IdEntity {

    protected Long id;

    @Id
//    @GeneratedValue(strategy = GenerationType.AUTO)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false)
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

}

 * 代碼中的SQL/HQL 更改 app

public List<LeakageDetail> findExceptLeakageDetailList(String ids) {
        String queryString = "SELECT * FROM leakage_detail "
                + "WHERE " // -- DATE_FORMAT(find_date, '%Y%m')<(DATE_FORMAT(NOW(), '%Y%m')-1) AND 
                + "CONCAT(find_date, find_process) IN ( "
                + "SELECT CONCAT(find_date, find_process) AS xx "
                + "FROM leakage_detail WHERE id IN(" + ids + ")"
                + "GROUP BY find_date, find_process "
//                + "HAVING COUNT(xx)>5)"; // 這種寫法MySQL支持,PostgreSQL不支持!
                + "HAVING COUNT(CONCAT(find_date, find_process))>5) AND id IN(" + ids + ") ORDER BY find_date, find_process";
        logger.info("Leakage模塊數據導入時發送漏液異常郵件的查詢sql->"+queryString);
        Query query = getSession().createSQLQuery(queryString).addEntity(LeakageDetail.class);
        return query.list();
    }

 

public List<StatisticalAnalysisVo> getStatisticalAnalysisList() {
//        String hql = "select workshop as name, count(id) as num from DataModel where date_format(create_at, '%Y-%m')=date_format(now(), '%Y-%m') group by workshop";
        String sql = "select workshop as name, count(id) as num "
                + "from data_models "
//                + "where date_format(create_at, '%Y-%m')=date_format(now(), '%Y-%m') " // date_format函數是MySQL專用的
                + "where to_char(create_at, 'yyyy-MM')=to_char(now(), 'yyyy-MM') " // PostgreSQL中的日期格式化函數是to_char
                + "group by workshop";
//        Query query = getSession().createSQLQuery(hql);
        Query query = getSession().createSQLQuery(sql).addScalar("name", Hibernate.STRING).addScalar("num", Hibernate.LONG);
        query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
            .setResultTransformer(Transformers.aliasToBean(StatisticalAnalysisVo.class));
        return query.list();
    }

* 存儲過程更改
MySQL ide

DROP PROCEDURE IF EXISTS `calcUlclp`;
DELIMITER //
CREATE DEFINER=`root`@`localhost` PROCEDURE `calcUlclp`()
    COMMENT '計算Hipot不良率的上下限的存儲過程'
BEGIN

SELECT (@rownum := @rownum + 1) AS `id`, DATE_FORMAT(lot_no_to_date, '%Y%m') AS year_and_month,
 `model_no`, group_no,
 SUM(liquid_injected_input_num) AS total_input,
 SUM(short_circuit_num) AS total_short,
 COUNT(DISTINCT(lot_no)) AS month_num_of_product_days
 ,ROUND(SUM(liquid_injected_input_num) / COUNT(DISTINCT(lot_no))) AS sample_size_n
 ,ROUND(SUM(short_circuit_num) / SUM(liquid_injected_input_num), 4) AS nonconforming_rate_mean_p
    FROM hipot, (SELECT @rownum := 0) AS r WHERE  liquid_injected_input_num!=0
	 GROUP BY `model_no`, group_no, year_and_month
;

END//
DELIMITER ;


PostgreSQL 函數

DROP FUNCTION IF EXISTS calcUlclp();

CREATE OR REPLACE FUNCTION calcUlclp()
    RETURNS SETOF record AS
$BODY$
 declare
--   sql varchar;
   rownum int;
   v_rc record;
BEGIN
  for v_rc in
SELECT (rownum = rownum + 1) AS id, to_char(lot_no_to_date, 'yyyyMM') AS year_and_month,
 model_no, group_no,
 SUM(liquid_injected_input_num) AS total_input,
 SUM(short_circuit_num) AS total_short,
 COUNT(DISTINCT(lot_no)) AS month_num_of_product_days
 ,ROUND(SUM(liquid_injected_input_num) / COUNT(DISTINCT(lot_no))) AS sample_size_n
 ,ROUND(SUM(short_circuit_num) / SUM(liquid_injected_input_num), 4) AS nonconforming_rate_mean_p
    FROM hipot, (SELECT rownum = 0) AS r WHERE  liquid_injected_input_num!=0
	 GROUP BY model_no, group_no, year_and_month
  loop
  return next v_rc;
  end loop;

END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

-- 調用存儲過程
/*
 SELECT * from calcUlclp() as
  t(id_ boolean, year_and_month text, model_no varchar, group_no varchar,
   total_input bigint, total_short numeric, month_num_of_product_days bigint,
    sample_size_n double precision, nonconforming_rate_mean_p numeric);
*/
相關文章
相關標籤/搜索