项目开发过程中经常需要编写数据库升级脚本来做一些基本的表操作:如添加表、添加字段,修改字段等。由于软件部署会经常执行数据库升级脚本,这就要求我们编写的升级脚本是可重复执行的。
基本思路是对表做操作前先判断表是否存在,对字段做操作前同样需要判断字段是否已经存在,避免脚本执行过程报错退出。下面基于脚本可重复执行的要求,给了一些常用的MYSQL数据库升级脚本编写样例:
DROP TABLE IF EXISTS tbl_user;
CREATE TABLE tbl_user (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL DEFAULT '',
age int(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS tbl_test3;
CREATE PROCEDURE add_tbl_user_name_en()
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_NAME='tbl_user'
AND TABLE_SCHEMA='testdb3'
AND COLUMN_NAME='name_en')
THEN
ALTER TABLE tbl_user ADD COLUMN name_en varchar(255) NOT NULL DEFAULT '' COMMENT '英文名';
END IF;
END;
CALL add_tbl_user_name_en;
DROP PROCEDURE add_tbl_user_name_en;
如何判断表中的字段是否已经存在?在mysql中没有什么简便的方法,笔者通过一个存储过程来做判断,处理完后再删除存储过程。
CREATE PROCEDURE change_tbl_user_name_en_to_name_cn()
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_NAME='tbl_user'
AND TABLE_SCHEMA='testdb3'
AND COLUMN_NAME='name_en')
THEN
ALTER TABLE tbl_user CHANGE name_en name_cn varchar(255) NOT NULL DEFAULT '' COMMENT '中文名';
END IF;
END;
CALL change_tbl_user_name_en_to_name_cn;
DROP PROCEDURE change_tbl_user_name_en_to_name_cn;
CREATE PROCEDURE delete_tbl_user_name()
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_NAME='tbl_user'
AND TABLE_SCHEMA='testdb3'
AND COLUMN_NAME='name')
THEN
ALTER TABLE tbl_user DROP name;
END IF;
END;
CALL delete_tbl_user_name;
DROP PROCEDURE delete_tbl_user_name;
同样,建库,建索引也需要提前判断库或者索引是否已经存在,避免重复创建。
数据库升级时也会有一些静态的配置数据要预置到数据库表中。在插入这些数据前需要执行一下delete或者TRUNCATE TABLE清除数据,避免多次执行脚本后插入重复数据。
一般来说,一台mysql服务器上会有多个db,每个db都会有自己的升级脚本。为了方便升级脚本自动化执行,在升级脚本前面需要加上”use testdb3”, 用于指定是对testdb3数据库操作。
软件部署后,数据库升级脚本一般都会通过下面这样的mysql命令自动执行:
E:\temp>mysql -h127.0.0.1 -P3306 -uroot < upgrade.sql
这样执行之前的存储过程会有语法问题(在navicat中是可以正常执行的),跟sql的load机制有关系。需要在编写脚本时通过自定义分割符来告诉存储过程的sql语句从哪里开始到哪里结束。下面以####作为分割符给一个样例:
use testdb3;
delimiter ####
CREATE PROCEDURE add_tbl_user_name_en()
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_NAME='tbl_user'
AND TABLE_SCHEMA='testdb3'
AND COLUMN_NAME='name_en')
THEN
ALTER TABLE tbl_user ADD COLUMN name_en varchar(255) NOT NULL DEFAULT '' COMMENT '英文名';
END IF;
END
####
delimiter ;
CALL add_tbl_user_name_en;
DROP PROCEDURE add_tbl_user_name_en;
SQL语法默认是以分号分割的,在存储过程前重定义为####,存储过程后需要恢复为分号。注意:分割符已经重定义为####,原来在存储过程最后一个END后面的那个分号就要去掉了。