1. 程式人生 > >mysql 外來鍵關聯限制

mysql 外來鍵關聯限制

第一次接觸mysql,很簡單的兩張表關聯,當然可以採用mysql自帶的外來鍵限制來保證資料一致性,但是上網查了些資料。發現對於insert/update操作完全合乎我的需求邏輯,但是delete卻與我需要的完全相反,比如A表(A_id),B表(B_id, A_id),其中A_id是B表外來鍵,A表主鍵,如果採用mysql中的FOREIGN KEY,並且是Restrict,則只有把B表刪除,才可以刪除A表,但是我的理解,配置的資料是先配置A表,再配B表,應該先刪A表,然後才能刪B表

不知道各位資料庫達人對這種外來鍵關聯關係有何高論,望回覆討論,多謝

mysql外來鍵詳解如下:

外來鍵約束對子表(上述B表)的含義: 


  如果在父表中找不到候選鍵,則不允許在子表上進行insert/update 

外來鍵約束對父表(上述A表)的含義: 
  在父表上進行update/delete以更新或刪除在子表中有一條或多條對應匹配行的候選鍵時,父表的行為取決於:在定義子表的外來鍵時指定的on update/on delete子句, InnoDB支援5種方式, 分列如下 

  . cascade方式 
   在父表上update/delete記錄時,同步update/delete掉子表的匹配記錄 
   On delete cascade從mysql3.23.50開始可用; on update cascade從mysql4.0.8開始可用 

  . set null方式 

   在父表上update/delete記錄時,將子表上匹配記錄的列設為null 
   要注意子表的外來鍵列不能為not null 
   On delete set null從mysql3.23.50開始可用; on update set null從mysql4.0.8開始可用 

  . No action方式 
   如果子表中有匹配的記錄,則不允許對父表對應候選鍵進行update/delete操作 
   這個是ANSI SQL-92標準,從mysql4.0.8開始支援 

  . Restrict方式 
   同no action, 都是立即檢查外來鍵約束 

  . Set default方式 
   解析器認識這個action,但Innodb不能識別,不知道是什麼意思... 

同時自己通過儲存過程實現了外表關聯過程如下:

USE pr;

CREATE TABLE IF NOT EXISTS ip_region
(
	begin_ip		int unsigned NOT NULL,
	end_ip			int unsigned NOT NULL,
	cluster_name	varchar(255) NOT NULL,
	isp				varchar(255) NULL,
	country			varchar(255) NULL,
	province		varchar(255) NULL,
	city			varchar(255) NULL,
	PRIMARY KEY (begin_ip, end_ip)
)ENGINE=InnoDB;


DROP PROCEDURE IF EXISTS add_ip_region;
DELIMITER //
CREATE PROCEDURE add_ip_region(
IN	p_begin_ip		int unsigned,
IN	p_end_ip		int unsigned,
IN	p_cluster_name	varchar(255),
IN	p_isp			varchar(255),
IN	p_country		varchar(255),
IN	p_province		varchar(255),
IN	p_city			varchar(255)
)
top:BEGIN
	DECLARE cluster_cnt	int unsigned default 0;
	SELECT COUNT(*)  INTO cluster_cnt FROM cluster_info WHERE cluster_name = p_cluster_name;
	IF (cluster_cnt = 0) THEN
		SELECT CONCAT('cluster_name: ', p_cluster_name, ' has not existed in cluster_info tbl');
		LEAVE top;
	END IF;

	INSERT INTO ip_region(begin_ip, end_ip, cluster_name, isp, country, province, city) VALUES 
			(p_begin_ip, p_end_ip, p_cluster_name, p_isp, p_country, p_province, p_city);
END //
DELIMITER ;



DROP PROCEDURE IF EXISTS up_ip_region;
DELIMITER //
CREATE PROCEDURE up_ip_region(
IN	p_begin_ip		int unsigned,
IN	p_end_ip		int unsigned,
IN	p_cluster_name	varchar(255),
IN	p_isp			varchar(255),
IN	p_country		varchar(255),
IN	p_province		varchar(255),
IN	p_city			varchar(255)
)
top:BEGIN
	DECLARE cluster_cnt	int unsigned default 0;
	SELECT COUNT(*)  INTO cluster_cnt FROM cluster_info WHERE cluster_name = p_cluster_name;
	IF (cluster_cnt = 0) THEN
		SELECT CONCAT('cluster_name: ', p_cluster_name, ' has not existed in cluster_info tbl');
		LEAVE top;
	END IF;

	UPDATE ip_region SET 
	cluster_name 	= CASE WHEN ISNULL(p_cluster_name) 	THEN cluster_name 	ELSE p_cluster_name END,
	isp 		= CASE WHEN ISNULL(p_isp) 		THEN isp 		ELSE p_isp 		END,
	country 	= CASE WHEN ISNULL(p_country) 		THEN country 		ELSE p_country 		END,
	province 	= CASE WHEN ISNULL(p_province)  	THEN province 		ELSE p_province  	END,
	city 		= CASE WHEN ISNULL(p_city)  	 	THEN city 		ELSE p_city  	  	END
	WHERE begin_ip = p_begin_ip and end_ip = p_end_ip;
END //
DELIMITER ;



DROP PROCEDURE IF EXISTS rm_ip_region;
DELIMITER //
CREATE PROCEDURE rm_ip_region(
IN	p_begin_ip		int unsigned,
IN	p_end_ip		int unsigned
)
top:BEGIN
	DECLARE cluster_cnt	int unsigned DEFAULT 0;
	
	SELECT COUNT(*) INTO cluster_cnt 
	FROM ip_region, cluster_info
	WHERE begin_ip = p_begin_ip AND end_ip = p_end_ip 
			AND ip_region.cluster_name = cluster_info.cluster_name;

	IF (cluster_cnt > 0) THEN
		SELECT CONCAT('the cluster has not been deleted from cluster_info tbl');
		LEAVE top;
	END IF;

	DELETE FROM ip_region WHERE begin_ip = p_begin_ip AND end_ip = p_end_ip;
END //
DELIMITER ;



DROP PROCEDURE IF EXISTS lst_ip_addr;
DELIMITER //
CREATE PROCEDURE lst_ip_addr(
IN	p_ip_addr	int unsigned,
OUT	p_begin_ip	int unsigned,
OUT	p_end_ip	int unsigned
)
BEGIN
	SELECT begin_ip, end_ip FROM ip_region 
	WHERE p_ip_addr >= begin_ip and p_ip_addr <= end_ip
	ORDER BY begin_ip DESC, end_ip LIMIT 1
	INTO p_begin_ip, p_end_ip;
END //
DELIMITER ;



DROP PROCEDURE IF EXISTS lst_ip_region;
DELIMITER //
CREATE PROCEDURE lst_ip_region(
IN	p_begin_ip	int unsigned,
IN	p_end_ip	int unsigned
)
BEGIN
	SELECT * FROM ip_region WHERE begin_ip = p_begin_ip and end_ip = p_end_ip;
END //
DELIMITER ;

USE pr;

CREATE TABLE IF NOT EXISTS cluster_info
(
	cluster_name	varchar(255)	NOT NULL,
	active	 		bool			NOT NULL,
	svr_1			varchar(255) 	NULL,
	svr_2			varchar(255) 	NULL,
	svr_3			varchar(255) 	NULL,
	svr_4			varchar(255) 	NULL,
	svr_5			varchar(255)  	NULL,
	svr_6			varchar(255) 	NULL,
	svr_7			varchar(255) 	NULL,
	svr_8			varchar(255) 	NULL,
	svr_9			varchar(255) 	NULL,
	svr_10			varchar(255) 	NULL,
	svr_11			varchar(255) 	NULL,
	svr_12			varchar(255) 	NULL,
	svr_13			varchar(255) 	NULL,
	svr_14			varchar(255) 	NULL,
	svr_15			varchar(255) 	NULL,
	svr_16			varchar(255) 	NULL,
	svr_17			varchar(255) 	NULL,
	svr_18			varchar(255) 	NULL,
	svr_19			varchar(255) 	NULL,
	svr_20			varchar(255) 	NULL,
	svr_21			varchar(255) 	NULL,
	svr_22			varchar(255) 	NULL,
	svr_23			varchar(255) 	NULL,
	svr_24			varchar(255) 	NULL,
	svr_25			varchar(255) 	NULL,
	svr_26			varchar(255) 	NULL,
	svr_27			varchar(255) 	NULL,
	svr_28			varchar(255) 	NULL,
	svr_29			varchar(255) 	NULL,
	svr_30			varchar(255) 	NULL,
	svr_31			varchar(255) 	NULL,
	svr_32			varchar(255) 	NULL,
	PRIMARY KEY (cluster_name)
)ENGINE=InnoDB;





DROP PROCEDURE IF EXISTS add_cluster_info;
DELIMITER //
CREATE PROCEDURE add_cluster_info(
IN	p_cluster_name	varchar(255),
IN	p_active		bool,
IN  p_svr_list		varchar(10000)
)
top: BEGIN
	DECLARE i 		  int unsigned 	 DEFAULT 1;
	DECLARE svr_cnt	  int unsigned 	 DEFAULT 0;
	DECLARE	svr_name  varchar(255) 	 DEFAULT '';
	DECLARE count	  int unsigned 	 DEFAULT 0;
	DECLARE svr_lst	  varchar(500)	 DEFAULT '';
	DECLARE svr_value varchar(10000) DEFAULT '';

	-- count the num of server
	SET svr_cnt = CHAR_LENGTH(p_svr_list) - CHAR_LENGTH(REPLACE(p_svr_list, ',', '')) + 1;
	IF svr_cnt > 32 THEN
		SELECT CONCAT('server count: ', svr_cnt, ' more than 32');
		LEAVE top;
	END IF;
	
	-- check if input server has been inserted into server_info tbl
	WHILE i <= svr_cnt DO
		SET svr_name = RTRIM(LTRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(p_svr_list, ',' , i), ',', -1)));
		SELECT COUNT(*) INTO count FROM server_info WHERE server_info.server_name = svr_name;
		IF count = 0 THEN
			SELECT CONCAT('svr_name: ', svr_name, ' has not been found in server_info tbl');
			LEAVE top;
		END IF;
		SET svr_lst = CONCAT(svr_lst, ', svr_', i);
		SET svr_value = CONCAT(svr_value, ', \'', svr_name, '\'');
		SET i = i + 1;
    END WHILE;

	-- concat sql str for adding cluster info
	SET @sql_str = CONCAT('INSERT INTO cluster_info (cluster_name, active', svr_lst, ')VALUES('
					'?, ?', svr_value, ');');
	SET @cn = p_cluster_name;
	SET @ac = p_active;
	PREPARE add_ex FROM @sql_str;
	EXECUTE add_ex USING @cn, @ac;
	DEALLOCATE PREPARE add_ex;
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS up_cluster_info;
DELIMITER //
CREATE PROCEDURE up_cluster_info(
IN	p_cluster_name	varchar(255),
IN	p_active		bool,
IN  p_svr_list		varchar(10000)
)
top:BEGIN
	DECLARE i 		 int unsigned 	DEFAULT 1;
	DECLARE svr_cnt	 int unsigned 	DEFAULT 0;
	DECLARE	svr_name varchar(255) 	DEFAULT '';
	DECLARE count	 int unsigned 	DEFAULT 0;

	-- count the num of server
	SET svr_cnt = CHAR_LENGTH(p_svr_list) - CHAR_LENGTH(REPLACE(p_svr_list, ',', '')) + 1;
	IF svr_cnt > 32 THEN
		SELECT CONCAT('server count: ', svr_cnt, ' more than 32');
		LEAVE top;
	END IF;

	-- check if input server has been inserted into server_info tbl	
	loop_label: WHILE i <= svr_cnt DO
		SET svr_name = RTRIM(LTRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(p_svr_list, ',' , i), ',', -1)));
		IF svr_name = '' THEN
			SET i = i + 1;
			ITERATE loop_label;
		END IF;

		SELECT COUNT(*) INTO count FROM server_info WHERE server_name = svr_name;
		IF count = 0 THEN
			SELECT CONCAT('svr_name: ', svr_name, ' has not been found in server_info tbl');
			LEAVE top;
		END IF;
		SET i = i + 1;
    END WHILE;

	-- update svr_name in cluster_tbl
	SET i = 1;
	loop_label: WHILE i <= svr_cnt DO
		SET svr_name = RTRIM(LTRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(p_svr_list, ',' , i), ',', -1)));
		IF svr_name = '' THEN
			SET i = i + 1;
			ITERATE loop_label;
		END IF;
		
		SET @sql_str = CONCAT('UPDATE cluster_info SET svr_', i, '=', '\'', svr_name, '\'', ' WHERE cluster_name = ?;');
		SET @cn = p_cluster_name;
		PREPARE up_ex FROM @sql_str;
		EXECUTE up_ex USING @cn;
		DEALLOCATE PREPARE up_ex;
		SET i = i + 1;
    END WHILE;

	-- update active in cluster_info
	UPDATE cluster_info SET 
	active = CASE WHEN ISNULL(p_active) THEN active ELSE p_active END
	WHERE cluster_name = p_cluster_name;
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS rm_cluster_info;
DELIMITER //
CREATE PROCEDURE rm_cluster_info(
IN	p_cluster_name	varchar(255)
)
top:BEGIN
	DECLARE count int unsigned DEFAULT 0;
	CALL cal_svr_count(p_cluster_name, count);
	IF count > 0 THEN
		SELECT CONCAT('the servers has not been deleted');
		LEAVE top;
	END IF;
	
	DELETE FROM cluster_info WHERE cluster_name = p_cluster_name;
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS lst_cluster_info;
DELIMITER //
CREATE PROCEDURE lst_cluster_info(
IN 	p_cluster_name 	varchar(255),
OUT	p_svr_cnt		int unsigned
)
BEGIN
	CALL cal_svr_count(p_cluster_name, p_svr_cnt);
	SELECT * FROM cluster_info WHERE cluster_name = p_cluster_name; 
END //
DELIMITER ;


DROP PROCEDURE IF EXISTS lst_all_cluster_info;
DELIMITER //
CREATE PROCEDURE lst_all_cluster_info(
)
BEGIN
	SELECT * FROM cluster_info;
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS cal_svr_count;
DELIMITER //
CREATE PROCEDURE cal_svr_count(
IN 	p_cluster_name 	varchar(255),
OUT	p_svr_cnt		int unsigned
)
BEGIN
	SELECT COUNT(*) INTO p_svr_cnt 
	FROM cluster_info, server_info
	WHERE (cluster_info.cluster_name = p_cluster_name) AND
	(cluster_info.svr_1 = server_info.server_name OR
	cluster_info.svr_2 = server_info.server_name OR
	cluster_info.svr_3 = server_info.server_name OR
	cluster_info.svr_4 = server_info.server_name OR
	cluster_info.svr_5 = server_info.server_name OR
	cluster_info.svr_6 = server_info.server_name OR
	cluster_info.svr_7 = server_info.server_name OR
	cluster_info.svr_8 = server_info.server_name OR
	cluster_info.svr_9 = server_info.server_name OR
	cluster_info.svr_10 = server_info.server_name OR
	cluster_info.svr_11 = server_info.server_name OR
	cluster_info.svr_12 = server_info.server_name OR
	cluster_info.svr_13 = server_info.server_name OR
	cluster_info.svr_14 = server_info.server_name OR
	cluster_info.svr_15 = server_info.server_name OR
	cluster_info.svr_16 = server_info.server_name OR
	cluster_info.svr_17 = server_info.server_name OR
	cluster_info.svr_18 = server_info.server_name OR
	cluster_info.svr_19 = server_info.server_name OR
	cluster_info.svr_20 = server_info.server_name OR
	cluster_info.svr_21 = server_info.server_name OR
	cluster_info.svr_22 = server_info.server_name OR
	cluster_info.svr_23 = server_info.server_name OR
	cluster_info.svr_24 = server_info.server_name OR
	cluster_info.svr_25 = server_info.server_name OR
	cluster_info.svr_26 = server_info.server_name OR
	cluster_info.svr_27 = server_info.server_name OR
	cluster_info.svr_28 = server_info.server_name OR
	cluster_info.svr_29 = server_info.server_name OR
	cluster_info.svr_30 = server_info.server_name OR
	cluster_info.svr_31 = server_info.server_name OR
	cluster_info.svr_32 = server_info.server_name );
END //
DELIMITER ;
USE pr;

CREATE TABLE IF NOT EXISTS server_info
(
	server_name varchar(255) NOT NULL,
	active		bool 		 NOT NULL,
    on_line		bool	     NOT NULL,
	svr_load	int unsigned NULL,
	max_cc_cnt	int unsigned NULL, -- max connect count
	cur_cc_cnt	int unsigned NULL, -- cur connect count
	fc_cc_cnt	int unsigned NULL, -- forecast connect count
	max_bw		int unsigned NULL, -- max bandwidth
	cur_bw		int unsigned NULL, -- cur bandwidth
	last_time	datetime	 NULL,
	server_ip	int unsigned NULL,
	svr_domain	varchar(255) NULL,
	noc_port	int unsigned NULL,
	ses_port	int unsigned NULL,
	priority	int unsigned NULL,
	
	PRIMARY KEY (server_name)
)ENGINE=InnoDB;




DROP PROCEDURE IF EXISTS add_server_info;
DELIMITER //
CREATE PROCEDURE add_server_info(
IN p_ser_name	varchar(255),
IN p_active		bool,
IN p_on_line	bool,
IN p_svr_load	int unsigned,
IN p_max_cc_cnt	int unsigned,
IN p_cur_cc_cnt	int unsigned,
IN p_fc_cc_cnt	int unsigned,
IN p_max_bw		int unsigned,
IN p_cur_bw		int unsigned,
IN p_last_time	datetime,
IN p_server_ip	int unsigned,
IN p_svr_domain	varchar(255),
IN p_noc_port	int unsigned,
IN p_ses_port	int unsigned,
IN p_priority	int unsigned
)
BEGIN
	INSERT INTO server_info 
		(server_name, active, on_line, svr_load, max_cc_cnt, cur_cc_cnt, fc_cc_cnt, 
		max_bw, cur_bw, last_time, server_ip, svr_domain,
		noc_port, ses_port, priority)
	values 
		(p_ser_name, p_active, p_on_line, p_svr_load, p_max_cc_cnt, p_cur_cc_cnt, p_fc_cc_cnt,
		p_max_bw, p_cur_bw, p_last_time, p_server_ip, p_svr_domain,
		p_noc_port, p_ses_port, p_priority);
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS up_server_info;
DELIMITER //
CREATE PROCEDURE up_server_info(
IN p_ser_name	varchar(255),
IN p_active		bool,
IN p_on_line	bool,
IN p_svr_load	int unsigned,
IN p_max_cc_cnt	int unsigned,
IN p_cur_cc_cnt	int unsigned,
IN p_fc_cc_cnt	int unsigned,
IN p_max_bw		int unsigned,
IN p_cur_bw		int unsigned,
IN p_last_time	datetime,
IN p_server_ip	int unsigned,
IN p_svr_domain	varchar(255),
IN p_noc_port	int unsigned,
IN p_ses_port	int unsigned,
IN p_priority	int unsigned
)
BEGIN
	UPDATE server_info SET 
	active 		= CASE WHEN ISNULL(p_active) 	 THEN active 		ELSE p_active 	  END,
	on_line 	= CASE WHEN ISNULL(p_on_line) 	 THEN on_line 		ELSE p_on_line 	  END,
	svr_load 	= CASE WHEN ISNULL(p_svr_load) 	 THEN svr_load 		ELSE p_svr_load   END,
	max_cc_cnt 	= CASE WHEN ISNULL(p_max_cc_cnt) THEN max_cc_cnt 	ELSE p_max_cc_cnt END,
	cur_cc_cnt 	= CASE WHEN ISNULL(p_cur_cc_cnt) THEN cur_cc_cnt 	ELSE p_cur_cc_cnt END,
	fc_cc_cnt 	= CASE WHEN ISNULL(p_fc_cc_cnt)  THEN fc_cc_cnt 	ELSE p_fc_cc_cnt  END,
	max_bw 		= CASE WHEN ISNULL(p_max_bw)  	 THEN max_bw 		ELSE p_max_bw  	  END,
	cur_bw 		= CASE WHEN ISNULL(p_cur_bw)  	 THEN cur_bw 		ELSE p_cur_bw  	  END,
	last_time 	= CASE WHEN ISNULL(p_last_time)  THEN last_time 	ELSE p_last_time  END,
	server_ip 	= CASE WHEN ISNULL(p_server_ip)  THEN server_ip 	ELSE p_server_ip  END,
	svr_domain 	= CASE WHEN ISNULL(p_svr_domain) THEN svr_domain 	ELSE p_svr_domain END,
	noc_port	= CASE WHEN ISNULL(p_noc_port)	 THEN noc_port		ELSE p_noc_port	  END,
	ses_port	= CASE WHEN ISNULL(p_ses_port)	 THEN ses_port		ELSE p_ses_port	  END, 
	priority 	= CASE WHEN ISNULL(p_priority)   THEN priority	 	ELSE p_priority	  END
	WHERE server_name = p_ser_name;
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS lst_all_server_info;
DELIMITER //
CREATE PROCEDURE lst_all_server_info()
BEGIN
	SELECT * FROM server_info;
END //
DELIMITER ;




DROP PROCEDURE IF EXISTS lst_server_info;
DELIMITER //
CREATE PROCEDURE lst_server_info(
IN p_ser_name	varchar(255)
)
BEGIN
	SELECT * FROM server_info WHERE server_name = p_ser_name;
END //
DELIMITER ;



DROP PROCEDURE IF EXISTS rm_server_info;
DELIMITER //
CREATE PROCEDURE rm_server_info(
IN p_ser_name	varchar(255)
)
BEGIN
	DELETE FROM server_info WHERE server_name = p_ser_name;
END //
DELIMITER ;