Mysql自動填充測試數據


前言

最近寫了兩個小腳本,一個應用於Mysql的自動填充測試數據,另外一個是bash寫的定期刪除日志文件,兩個腳本如何使用,在GitHub上面都有所說明,這里不再贅述,這里主要是想聊一下Mysql存儲過程以及自動填充測試數據。
為什么要寫一個自動填充測試數據的腳本?
網上其實也有一些簡單的給Mysql填充數據的博客,但是大多數都是針對於特定的表的特定數據來實現,寫的過於簡單,實用性不強,而這個腳本可以根據我們提供表的字段,來自動識別我們的字段並填充進入對應的內容,常用的結構都能夠滿足,當然還有進一步完善的空間。

存儲過程

Mysql的存儲過程可以幫助我們實現一些較為復雜的業務邏輯,就像我們在PHP或者其他語言中所寫的邏輯一樣,在Mysql中也同樣可以執行,比如我們要循環寫入1000行數據。
不過雖然Mysql可以實現,但是我們更希望把業務邏輯建立在我們的業務語言上面,而Mysql則專注於處理數據的CURD,其主要原因在於我們今后在修改或者要查找到這塊的業務邏輯時,會相對麻煩一些。
因此,我更傾向於Mysql可以用來做一些與我們的業務邏輯無關,而又需要用到語言的邏輯性的項目,比如我在前面提到的自動填充數據。

形式

delimiter $$
drop procedure if exists test $$
create procedure test()
begin 
-- do something
end $$
delimiter ;

這是一個自動識別表結構並填充數據的腳本,在第一行使用了delimiter $$用作分隔符,因為接下來的腳本里面很多地方會使用到;,所以為防止過早的識別結束,暫時修改了Mysql默認的分隔符。
此外,前面有beginend包裹,用於識別開始和結束。
暫時先不講邏輯怎么實現的(其實邏輯也非常簡單),我們先來了解一下Mysql的存儲過程的幾個部分。
注:通過show create procedure your_procedure_name可以查看創建的存儲過程的代碼內容。

變量

Mysql的變量分成三種:全局變量,用戶變量,局部變量。

  • 局部變量

局部變量存在於我們的存儲過程中,在外面無法訪問:

delimiter $$
drop procedure if exists test $$
create procedure test()
begin 
	declare myName varchar(8) default 'seven';
	set myName = 'nine';
	select myName;
end $$
delimiter ;
call test();

結果輸出nine,因為在用戶變量中需要@來聲明,所以此時在外面無法使用select myName,否則報錯。

  • 用戶變量

用戶變量存在於全局,但是有效期僅限於會話期(所以也可以叫做會話變量),即我們下次打開Mysql時,變量就會消失:

delimiter $$
drop procedure if exists test $$
create procedure test()
begin 
	set @myName = 'nine';
end $$
delimiter ;
call test();
select @myName ;

輸出nine

當然,賦值的形式是多樣的,我們也可以結合查詢語句來賦值:

select name, age from user limit 1 into @myName , @myAge;
select @myName , @myAge;

這個時候賦值的內容就是我們表中的數據,這里需要注意的是一一對應關系,切不可一對多或者多對一。

在某些時候,我們可能需要對查詢的次數進行記錄,其實這個時候我們可以完全使用變量來幫我們實現:

set @selectNum = 0;
select * , @selectNum := @selectNum + 1 from user limit 1;

這個時候@selectNum的結果為2。

  • 全局變量

全局變量是設置在系統中的配置,我們可以通過show global variables來查看,也可以通過set global oneofvariable=value來設置我們已經存在的配置,這里我特意給已經存在這幾個字眼加粗,因為我在網上看到有博文說可以設置自定義的全局變量,但是我嘗試了之后發現報錯了,我用版本5.55.7都嘗試過了,提示這個變量不存在。

參數

參數分成INOUT以及INOUT三種情況:
其實從其字面上我們也能猜出他們的不同效果:IN是不會影響外面設置的結果(IN是默認方式),OUTINOUT是會影響到的,同時INOUT兩邊是相互影響的,我們還是以上面的test為例:

  • in
set @num = 1;
delimiter $$
drop procedure if exists test $$
create procedure test(num int)
begin 
	set num = 2;	
end $$
delimiter ;
call test(@num);
select @num;

結果為1。

  • out
set @num = 1;
delimiter $$
drop procedure if exists test $$
create procedure test(OUT num int)
begin 
	set num = 2;	
end $$
delimiter ;
call test(@num);
select @num;

結果為2。

語句塊

這篇文章關於語句塊已經闡述的足夠詳細,這里不再贅述。

實現

代碼:

delimiter $$
drop procedure if exists fillTable $$
create procedure fillTable(in num int  , in tbName varchar(16))
begin 
-- 獲取當前數據庫
	select (@dbName:=database());
	set @tbName = tbName;
-- 獲取表的字段總數
	set @currSql = "select count(1) from information_schema.COLUMNS where table_name = ? and table_schema = ? into @columnSum";
	prepare stmt from @currSql;
	execute stmt using @tbName , @dbName;
	deallocate prepare stmt;
	
	set @currNum = 0;
	
-- 這里設置隨機的字符串
	set @chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	
	while @currNum < num do 
		-- 這里設置sql后面拼接
		set @insertSql = concat("insert into " , @tbName , " values ( ");
		set @columnNum = 1;
		while @columnNum <= @columnSum do
			set @value := '';
			set @currSql = "select (@column := COLUMN_NAME) , (@length := CHARACTER_MAXIMUM_LENGTH) , (@key := COLUMN_KEY) , (@type := DATA_TYPE) from information_schema.COLUMNS where table_name = ? and table_schema = ? limit ?";
			prepare stmt from @currSql;
			execute stmt using @tbName , @dbName , @columnNum;
			deallocate prepare stmt;
			
	-- 根據類型來填充數據
			if right(@type , 3) = 'int' then
				if @type = 'int' then
					set @value = 'default';
				else 
					set @value = floor(rand() * 100);
				end if;
			
			elseif right(@type , 4) = 'char' then
				set @counter = 0;
				while @counter < @length do    
	        		set @value = concat(@value,substr(@chars,ceil(rand()*(length(@chars)-1)),1));  
	    			set @counter = @counter + 1;
	    		end while;
	    		
	    		set @value = concat("'" , @value , "'");
	    		
	    	elseif @type = 'blob' or right(@type , 4) = 'text' then
	    		set @counter = 0;
	    	 	while @counter < 100 do    
	        		set @value = concat(@value,substr(@chars,ceil(rand()*(length(@chars)-1)),1));  
	    			set @counter = @counter + 1;
	    		end while;
	    		
	    		set @value = concat("'" , @value , "'");
	    		
	    	elseif @type = 'float' or @type = 'decimal' then
	    		set @value = round(rand() , 2);
	    	else 
	    		set @value = 'nine';
	    	end if;
	    	
	-- 判斷這個數是否是最后一個
			if @columnNum = @columnSum then
				set @insertSql = concat(@insertSql , @value , ' )');
			else 
				set @insertSql = concat(@insertSql , @value , ' , ');
			end if;
			
			set @columnNum = @columnNum + 1;
		end while;
	-- 執行
		prepare stmt from @insertSql;
		execute stmt;
		deallocate prepare stmt;
		
		set @currNum = @currNum + 1;
	
	end while;
	
end $$
delimiter ;

其實實現這個功能的邏輯非常簡單,再各個步驟里面也附上了步驟,主要是利用了系統的information_schema.COLUMNS表來獲取我們需要的一些基本信息,主要結構如下圖所示:
81b12bcd-e426-47e7-b7ab-1f96e034826a.png

參考

mysql存儲過程詳細教程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM