產品即將上線,可項目經理說要換數據庫,要把mysql數據庫的內容全部搬到Postgresql中去。
有一個python的工具可以實現遷移(但是存儲過程無法遷移、數據類型也無法靈活對應,還要改程序),爲了節約時間採用人工遷移的方式,閒話少說,以下是遷移過程,沒圖,對不起。
利用Navicat for Mysql工具(其它工具也可以,比如mysqldump)將數據庫模式導出(只導表結構不含數據)方法略,生成的文件名爲mysql.sql
利用linux sed工具把導出的腳本中的所有COMMENT及mysql的獨有原素替換掉,並生成新文件table_new.sql,見以下腳本
cat mytab.sql | sed 's/^.*ENGINE=.*$/);/g' | sed 's/COMMENT.*$/,/g' | sed 's/`//g' > table_new.sql將所有的INDEX語句單獨生成一個文件(此步也可以省略,後面有其它方法)
cat table_new.sql | egrep '\-|^\s*KEY|^\s*UNIQUE' > key.sql將所有的外鍵語句單獨生成一個文件 (此步可以省略,後面有其它方法)
cat table_new.sql | egrep '\-|^\s*CONSTRAINT' > constraint.sql去掉KEY和CONSTRAINT的語句,並生成新文件 table_last.sql
cat table_new.sql | sed 's/^\s*KEY.*$//' | sed 's/^\s*CONSTRAINT.*$//' | sed 's/^\s*UNIQUE.*$//' | sed '/^$/d'> table_last.sql
轉換換行符
unix2dos table_last.sql
把文件COPY到WINDOWS中,用記事本工具(或其它工具)對一些特殊值進行查找、替換操作(此步的意思就是把mysql中的類型對應到postgresql中的類型),比如:
查找 ID int(12) NOT NULL AUTO_INCREMENT 替換 ID bigserial NOT NULL (自增長類型字段)
查找 datetime 替換 timestamp
……
重新整理一些特殊表,因爲第5步中去掉了所有KEY和CONSTRAINT的語句,所以如果表字段中包括KEY開頭或是CONSTRAINT開頭的字段時(比如字段 KEYCD,KEYTTL等)要把這些特殊的表的建表語句手工製作一變,並在table_last.sql中替換掉原來的語句。
在Postgresql中執行table_last.sql語句(方法略),如果有錯誤進行微調,直到所有表建立成功。
導入數據(先在mysql中導出,導出後的文件要替換換行符不然報錯,然後再導入到postgresql)
導出語句(在MYSQL中執行以下語句,並複製結果後再次執行,便會在服務器的目錄下生成以每個表爲單位的數據文件,我用的工具是Navicat for Mysql。注意替換table_schema,這裏生成數據文件的目錄爲:/usr/local/mysql/outfile/,也可換成其它目錄)
SELECT CONCAT('select * from ', table_name, " into outfile '/usr/local/mysql/outfile/", table_name , '.dat' ,"'" " fields terminated by '|' ;") FROM information_schema.tables WHERE table_schema='DEVELOP';
替換換行符後將生成的文件COPY到postgresql服務器的目錄下
sed -i 's/\r//g' outfile/*導入語句(在postgresql中執行,得到查詢結果後複製查詢結果,並再次執行,注意一定要用有superuser權限的用戶執行,這裏用的是pgadmin工具,目錄是/tmp/data,注意替換table_catalog中的值,我的數據名爲DEVELOP)
select 'copy necsl.'||table_name|| ' from ' || chr(39)||'/tmp/data/' ||upper(table_name)|| '.dat' || chr(39) || ' with DELIMITER ' || chr(39) || '|' || chr(39) ||';' from information_schema.tables where table_schema='necsl' and table_catalog='DEVELOP' ;
數據導入後可以用以下方法驗證是否導入正確
--在mysql及PG中分別建立下表 create table table_count(tblname varchar(100) primary key not null,tblrecorder integer);
/*在mysql中執行以下語句,並將結果複製後再次執行,將會生成在表table_count中生成每張表有多少記錄*/ SELECT CONCAT('insert into table_count select ' ,"'",table_name,"'",' ,count(*) from ', table_name ,";") FROM information_schema.tables WHERE table_schema='DEVELOP'; --在postgresql中執行以下語句,並將結果複製後再次執行,將會生成在表table_count中生成每張表有多少記錄,然後與mysql中table_count的表比較即可 select 'insert into necsl.table_count select '|| quote_literal(table_name) ||',count(*) from necsl.'||table_name|| ';' from information_schema.tables where table_schema='necsl' and table_catalog='DEVELOP' ;
--如果導入有問題,可以在PG中執行以下句語生成清空所有表的語句,將執行結果複製再執行即可清空所有表 PG truncate select 'truncate table necsl.'||table_name|| ';' from information_schema.tables where table_schema='necsl' and table_catalog='DEVELOP' ;
/*生成索引信息及外鍵,在mysql中分別執行以下語句,並將結果複製到POSTGRESQL中執行*/ /*生成唯一索引*/ select CONCAT ( 'CREATE UNIQUE INDEX ' , table_name,'_IDX_',index_name, ' ON ', table_name, '(', GROUP_CONCAT(column_name order by seq_in_index), ');' ) from statistics where table_schema='DEVELOP' AND INDEX_NAME <>'PRIMARY' AND INDEX_SCHEMA='DEVELOP' AND NON_UNIQUE=0 GROUP BY index_name,table_name ORDER BY TABLE_NAME,INDEX_NAME,seq_in_index asc /*生成btree索引*/ select CONCAT ( 'CREATE INDEX ' , table_name,'_IDX_',index_name, ' ON ', table_name, '(', GROUP_CONCAT(column_name order by seq_in_index), ');' ) from statistics where table_schema='DEVELOP' AND INDEX_NAME <>'PRIMARY' AND INDEX_SCHEMA='DEVELOP' AND NON_UNIQUE=1 GROUP BY index_name,table_name ORDER BY TABLE_NAME,INDEX_NAME,seq_in_index asc /*生成外鍵 */ select concat('alter table ', c.TABLE_NAME, ' add constraint ', c.CONSTRAINT_NAME, ' foreign key(', c.COLUMN_NAME, ') references ', c.REFERENCED_TABLE_NAME, '( ', c.REFERENCED_COLUMN_NAME, ');') from TABLE_CONSTRAINTS t,KEY_COLUMN_USAGE c where t.CONSTRAINT_SCHEMA='DEVELOP' AND t.CONSTRAINT_TYPE='FOREIGN KEY' AND t.TABLE_SCHEMA='DEVELOP' AND c.REFERENCED_TABLE_SCHEMA='DEVELOP' AND t.CONSTRAINT_NAME =c.CONSTRAINT_NAME and t.table_name=c.table_name;
17.生成以上信息後,在PG中建立以下函數,用於重置所有的sequence(如果沒有自增長類型可忽略此步),建立放在public模式下
CREATE OR REPLACE FUNCTION pro_resetallseq() RETURNS void AS $BODY$ DECLARE tmp VARCHAR(512); maxval bigint; stmt record; BEGIN FOR stmt IN select sequence_name,REPLACE(sequence_name,'_id_seq','') as tnm from information_schema.sequences where sequence_catalog='DEVELOP' and sequence_schema='necsl' LOOP tmp := 'SELECT MAX(ID) FROM '|| quote_ident(stmt.tnm) || ';'; EXECUTE tmp into maxval; if not (maxval is NULL or maxval = 0) then EXECUTE 'SELECT SETVAL('|| quote_literal(stmt.sequence_name) || ',' || maxval || ');'; end if; END LOOP; RAISE NOTICE 'finished .....'; END; $BODY$ LANGUAGE plpgsql
18.建立成功後可以用以下語句重置所有的sequence
select pro_resetallseq();
19.關於mysql中的函數(存儲過程)因爲語法差別較大,只能在PG中重寫。我沒有找到太好的方法。
因爲時間緊,所以沒有寫工具,過程也比較粗糙,僅供參考,如果有好的建議歡迎留言,謝謝閱讀。