多列上的MySQL外键?
问题描述:
我还在学习MySQL,所以请忍受我...多列上的MySQL外键?
我有几个表(下面概述),我想记录错误消息。我不是指查询错误,而只是我的消息,如“连接到站点失败”,“在站点xyz卡读取错误”等...
我想到有一个表“error_logs”跟踪我的错误,只有三列:
- ID
- ERROR_MESSAGE
- IS_ACTIVE
我遇到的问题,然而,就是每个错误会涉及到创纪录在'卡','门'或'网站'表,我想跟踪消息属于哪条记录(来自各种表格)。
所以,我的问题是什么将是最好的方法来解决这个问题?我曾考虑添加两个额外的列,'record_id'(它将引用另一个表中的一个id)和'table'(它将跟踪消息所属的表)。另外,另一个警告是,很可能会有额外的“主要”表格。
这里是我迄今表:
CREATE TABLE `statuses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1$$;
INSERT INTO statuses SET name = 'error';
INSERT INTO statuses SET name = 'pre-install';
INSERT INTO statuses SET name = 'validate';
INSERT INTO statuses SET name = 'active';
INSERT INTO statuses SET name = 'disabled';
CREATE TABLE `sites` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`status_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `sites_status_id_idx` (`status_id`),
CONSTRAINT `sites_status_id` FOREIGN KEY (`status_id`) REFERENCES `statuses` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$;
CREATE TABLE `doors` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(255) NOT NULL,
`site_id` int(11) NOT NULL,
`status_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `doors_site_id_idx` (`site_id`),
KEY `doors_status_id_idx` (`status_id`),
CONSTRAINT `doors_status_id` FOREIGN KEY (`status_id`) REFERENCES `statuses` (`id`) ON UPDATE CASCADE,
CONSTRAINT `doors_site_id` FOREIGN KEY (`site_id`) REFERENCES `sites` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$;
CREATE TABLE `card_types` (
`id` int(11) NOT NULL,
`manufacturer` varchar(45) DEFAULT NULL,
`sequance` varchar(32) DEFAULT NULL,
`auth_code` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$;
CREATE TABLE `cards` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`number` int(11) NOT NULL,
`type_id` int(11) NOT NULL,
`status_id` int(11) NOT NULL,
`is_oem` bit(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `number_UNIQUE` (`number`),
KEY `cards_type_id_idx` (`type_id`),
KEY `cards_status_id_idx` (`status_id`),
CONSTRAINT `cards_status_id` FOREIGN KEY (`status_id`) REFERENCES `statuses` (`id`) ON UPDATE CASCADE,
CONSTRAINT `cards_type_id` FOREIGN KEY (`type_id`) REFERENCES `card_types` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$;
CREATE TABLE `card_assignments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`door_id` int(11) NOT NULL,
`status_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `card_assignments_door_id_idx` (`door_id`),
KEY `card_assignments_status_id_idx` (`status_id`),
CONSTRAINT `card_assignments_door_id` FOREIGN KEY (`door_id`) REFERENCES `doors` (`id`) ON UPDATE CASCADE,
CONSTRAINT `card_assignments_status_id` FOREIGN KEY (`status_id`) REFERENCES `statuses` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$;
答
我觉得你的建议是完全有效的。请务必为您的table_name
字段选择一个enum
数据类型;这样,不同的表名得到 由MySQL
透明地映射到整数值。这将使用比VARCHAR
字段 更少的内存,并且与特定表相关的选择错误将会更快。额外的 INDEX
/KEY
超过table_name
和record_id
将进一步提高性能。您可以创建表如下:
CREATE TABLE IF NOT EXISTS `error_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`table_name` enum('sites','tables','doors') CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
`record_id` int(11) NOT NULL DEFAULT '0',
`error_message` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
`is_active` tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `table_name` (`table_name`,`record_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
选择由table_name
错误是那么容易,因为:
SELECT * FROM error_logs WHERE table_name = 'sites'
当添加额外的实体和表格中提到,记得要适当延长table_name
enum
。
的替代,我觉得有些差,你的情况是使用不同的ID字段每个主表:
CREATE TABLE IF NOT EXISTS `error_logs2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cards_id` int(11) DEFAULT NULL,
`doors_id` int(11) DEFAULT NULL,
`sites_id` int(11) DEFAULT NULL,
`error_message` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
`is_active` tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `cards_id` (`cards_id`),
KEY `doors_id` (`doors_id`),
KEY `sites_id` (`sites_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
在这里,只有ID字段中的一个将包含一个值,而另一个两个将是NULL
。对于插入相关网站表和记录ID 1的错误:
INSERT INTO `error_logs2` (`id`, `cards_id`, `doors_id`, `sites_id`, `error_message`, `is_active`)
VALUES (NULL, NULL, NULL, '1', 'Exception has occurred: Division by zero.', '1')
选择网站的错误会的工作如下:
SELECT * FROM error_logs2 WHERE sites_id IS NOT NULL
但是,为了能够涉及错误到一个新的实体,您” d必须在error_logs2
表中添加一个新列。但是,为一个特定实体和id选择错误只需要一个字段比较,例如:
SELECT * FROM error_logs2 WHERE cards_id = 5
马塞勒斯,非常感谢您的意见!我将利用您的初始建议并创建表格,如同列表中列出的enum数据类型一样。谢谢。 – Kate 2013-04-30 21:54:45
不客气,很高兴帮助:) – Marcellus 2013-05-01 14:19:58