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 ;