原文:https://blog.csdn.net/szulilin/article/details/101034889
文章目錄
背景
環境
migration的指令
第一步:創建遷移文件
第二步:修改遷移文件,寫入需要做的事情
第三步:執行遷移(數據庫變更)
解決指定索引長度
解決不支持枚舉類型
執行sql update
總結
背景
數據庫遷移也是laravel強大的功能之一,但是一直也只是在文檔上看過,實際項目中還沒有使用過,因為需要遷移的場景比較少,而且需要遷移的時候,直接mysqldump也很方便,好像不是很有必要。
但是最近遇到一個問題,開發不能登機器操作數據庫了,這就比較難受了,一般發布新版本,都會有一些數據庫變更的操作,常見的有:加表,加字段,加索引,初始化數據,修歷史數據。當然改字段一般是不會改,除非特殊情況,改表就更不可能了,不能登機器怎么操作啊?寫個腳本跑一下?但是總不能每次發布都加個腳本吧?這里就想到了migration好像有這種功能,所以這里在本地實踐了一下:
環境
php:7.0
lumen:5.3
mysql:5.6
windows:10
migration的指令
windows上findstr類似於linux上的grep
php artisan migrate:install 新建一個遷移倉庫,會在數據庫中生成一個表migrations,第一次初始化的時候執行
表結構如下:
CREATE TABLE `migrations` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`migration` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`batch` INT(11) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
php artisan make:migration {migration_name} {--create=table_name} {--table=table_name} {--path=file_path} 新建一個遷移文件到file_path(使用默認的就好)目錄,
文件名格式:date('Y_m_d_His')_{migration_name}.php
類名為:migration_name按_或者-分割后首字母大寫,migration_name不可重復
--table:指定操作的表名,修改表使用這個參數
--create:指定創建的表名,新增表使用這個參數
--path:使用默認的路徑database/migrations,不用傳此參數
php artisan migrate {--step} {--pretend} {--force} 運行數據庫遷移,按文件前面的時間,從小到大執行,如果正常執行結束,在migrations表中新增記錄,batch值遞增
--step:若有此參數,每一個文件batch會遞增一次,回滾的時候可以每次回滾一個文件,如果沒有此參數,一次執行多個文件會使用同一個batch值,只能多個文件一起回滾
--pretend:加了這個參數,不會真正執行遷移,會輸出遷移需要執行的sql,參數沒有綁定,會輸出【?】
--force:production環境下強制執行(如果沒有帶該參數,會詢問是否強制執行)
php artisan migrate:status 顯示每條遷移的狀態(是否已經遷移)
php artisan migrate:refresh {--step} {--force} 會執行所有遷移文件的回滾操作(可以指定回滾的step),然后執行遷移操作(指定step的話,只會執行回滾的那幾步,否則全部執行一遍)
--step:回滾幾步,沒有全部回滾
--force:production環境下強制執行(如果沒有帶該參數,會詢問是否強制執行)
php artisan migrate:reset {--step} {--pretend} {--force} 會執行所有遷移文件的回滾操作(可以指定回滾的step)
--step:回滾幾步,沒有全部回滾
--force:production環境下強制執行(如果沒有帶該參數,會詢問是否強制執行)
--pretend:加了這個參數,不會真正執行遷移,會輸出遷移需要執行的sql,參數沒有綁定,會輸出【?】
php artisan migrate:rollback {--step} {--pretend} 只回滾最后一條遷移
--step:回滾幾步,沒有回滾最后一步
--pretend:加了這個參數,不會真正執行遷移,會輸出遷移需要執行的sql,參數沒有綁定,會輸出【?】
后面的英文已經標注了migrate命令的用法,這里實際操作一下:
按順序:
第一步:創建遷移文件
php artisan make:migration create_table_user --table=user
//后面的參數分別表示一個遷移文件的標識,和指定需要操作的表
php artisan make:migration create_table_user --create=user
//后面的參數分別表示一個遷移文件的標識,和需要創建的表
執行后會生成一個遷移文件如圖
每一個文件根據第一個參數會生成一個類,所以這里不能傳入重名的參數,而且不能以數字開始,需要符合類的命名規范
第二步:修改遷移文件,寫入需要做的事情
這里來點實際操作的,假設我要新增一張表,user,有r這些字段,id,name,account,create_at,update_at,delete_at
代碼如下
public function up()
{
Schema::create('user', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 50)->default('');
$table->string('account', 50)->default('');
$table->tinyInteger('status')->default(0);
$table->mediumInteger('status1')->default(0);
$table->integer('create_at')->default(0);
$table->integer('update_at')->default(0);
$table->integer('delete_at')->default(0);
$table->primary('id');
$table->unique('account','account');
$table->index(['name', 'delete_at'],'index_name');
});
}
第三步:執行遷移(數據庫變更)
php artisan migrate //該指令會執行未執行過的遷移文件
這里我是沒看出什么問題,但是報錯了,而且表建成功了
CREATE TABLE `user` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
`account` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
`status` TINYINT(4) NOT NULL DEFAULT 0,
`status1` MEDIUMINT(9) NOT NULL DEFAULT 0,
`create_at` INT(11) NOT NULL DEFAULT 0,
`update_at` INT(11) NOT NULL DEFAULT 0,
`delete_at` INT(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;
這就很尷尬了,執行回滾也不行,異常了,還沒寫入數據庫,只能先刪表了(這在線上肯定不能這么搞),
問題一
把主鍵的去掉,再執行一下
public function up()
{
Schema::create('user', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 50);
$table->string('account', 50)->default('');
$table->tinyInteger('status')->default(0);
$table->mediumInteger('status1')->default(0);
$table->integer('create_at');
$table->integer('update_at')->default(0)->unique();
$table->integer('delete_at')->default(0);
$table->unique('account','account');
$table->index(['name', 'delete_at'],'index_name');
});
}
CREATE TABLE `user` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
`account` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
`status` TINYINT(4) NOT NULL DEFAULT 0,
`status1` MEDIUMINT(9) NOT NULL DEFAULT 0,
`create_at` INT(11) NOT NULL,
`update_at` INT(11) NOT NULL DEFAULT 0,
`delete_at` INT(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `account` (`account`),
UNIQUE INDEX `user_update_at_unique` (`update_at`),
INDEX `index_name` (`name`, `delete_at`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;
建表算是成功了,但是看了一下源碼發現有如下情況:
1,默認所有字段都是not null ,其他的unsiged, default,comment都是沒有的,需要手動加
2,字符集默認使用數據庫配置的字符集,也可以自己指定,表引擎也是一樣
3,int沒有指定位數,只有int bigint dediumint tinyint
4,increments自動當作pramiry key,而且后面再加上會報錯
5,rollback風險很高,除了rollback,還有reset,refresh同樣風險很高,建議線上關閉
6,索引會當作一條單獨的語句
看了源碼算是有個底了,因此繼續實踐,那么現在的目標如下:
1,新建三張線上字段比較多的表
2,給三張表,各新增一個字段,並加上索引
3,給三個字段初始化一定的數據
4,給原表的字段修改一些數據,並做備份
5,感覺封裝有點問題,nullable那些還要去查,不能
6,索引算法如果沒有指定,使用默認的
執行腳本生成遷移文件:
php artisan make:migration create_table_employee --create=employee
public function up()
{
Schema::connection("mysql");
Schema::create('employee', function (Blueprint $table) {
$table->engine = "innodb";
$table->charset = "utf8";
$table->collation = "utf8_general_ci";
$table->increments('id');
$table->integer('company_id')->default(0)->comment("company_id");
$table->string('uid', 2058)->default('')->comment("uid");
$table->string('account_id', 2058)->default('')->comment("account_id");
$table->string('user_id', 2058)->default('')->comment("user_id");
$table->string('name', 2058)->default('')->comment("name");
$table->enum('id_type', [1,2])->default(1)->comment("id_type");
$table->string('id_code', 2058)->default('')->comment("id_code");
$table->enum('status', ['IN','OUT'])->default('IN')->comment("status");
$table->string('field_id_7', 500)->default('')->comment("field_id_7");
$table->integer('delete_time')->default(0)->comment("delete_time");
$table->integer('update_time')->default(0)->comment("update_time");
$table->unique(['uid','company_id','delete_time'],'company_id_uid');
$table->unique(['company_id','id_code(136)','id_type','delete_time'],'company_id');
$table->index(['company_id','name(136)','delete_time'],'company_id_name');
$table->index(['company_id','user_id(136)','delete_time'],'company_id_user_id');
});
}
執行一下,有報錯,
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long;
max key length is 3072 bytes
又是很尷尬了,索引長度太長了,這里需要指定索引長度的,但是怎么指定???
sql是在字段后面加個field(10),這里加了也沒有,又是一個坑,還能不能愉快的玩耍了???,搜了一下好像解決不了,這種特殊的只能直接執行statement了,
解決指定索引長度
//這里有有一種特殊的情況,指定索引長度(加密的uid等字段需要),不支持,可以這樣寫
$connection = DB::connection("mysql");
$sql = 'ALTER TABLE `table_name` ADD index `index_name2` (`id`, `name`(10), `u_id`) ';
$connection->statement($sql);
解決不支持枚舉類型
$connection = DB::connection("mysql");
$sql = 'desc `table_name` `type`';
$ret = $connection->select($sql);
$typeEnumList = explode(',',str_replace(['(',')','enum'],'',$ret[0]->Type));
$typeEnumList[] = "'new_type'";
$typeEnumStr = implode(',', $typeEnumList);
$sql = "ALTER TABLE `table_name` CHANGE COLUMN `type` `type` ENUM($typeEnumStr) NOT NULL DEFAULT 'type1'";
$connection->statement($sql);
執行sql update
$connection = DB::connection("mysql");
$sql = 'select * from `table_name` where `id` = 10;';
$ret = $connection->statement($sql);
$sql = 'update `table_name` set `name` = \'name\' where `id` = 10';
$connection->statement($sql);
總結
1,lumen 的migration初次使用還是有點不太順利,主要原因還是不熟悉,后面看了幾遍源碼之后,migration的規則基本就清楚了,寫起來也就更加得心應手了
2,注意線上執行的話,要加異常處理,否則不知道執行到哪一步失敗了,然后又重復執行了可能出現不可預期的結果
3,rollback這種功能,會刪表的,建議生產環境禁用吧,否則不小心執行了后果很嚴重
4,有一些類型不支持,像枚舉和索引長度,這種可以換成直接執行statement執行sql也能解決
5,多看源碼,也花不了多少時間,有些指令的參數,官方文檔中並沒有寫,但是很有用,使用好的話,可以防止踩很多坑
————————————————
版權聲明:本文為CSDN博主「szulilin」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/szulilin/java/article/details/101034889