2013-04-30 3 views
1

Я все еще изучаю MySQL, поэтому, пожалуйста, медведь со мной ...Внешний ключ MySQL на нескольких столбцах?

У меня есть несколько таблиц (очерченных ниже), для которых я хотел бы регистрировать сообщения об ошибках. Я не имею в виду ошибки запроса, а скорее мои сообщения, такие как «соединение с сайтом не удалось», «ошибка чтения карты на сайте xyz» и т. Д.

Я думал о наличии таблицы "error_logs" следить за моих ошибок только с тремя колонками:

  • Ид
  • error_message
  • is_active

проблема я встречая, однако, заключается в том, что каждая ошибка будет относиться к записи в таблицы «карты», «двери» или «сайты», и я хотел бы отслеживать, какую запись (из разных таблиц) принадлежит сообщению.

Итак, мой вопрос в том, что было бы лучшим способом приблизиться к этому? Я рассмотрел добавление двух дополнительных столбцов: «record_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$$; 

ответ

0

Я думаю, что ваше предложение вполне допустимо. Просто не забудьте выбрать тип данных enum для вашего поля table_name; таким образом, разные имена таблиц получают , преобразованные в целые значения прозрачно на MySQL. Это будет использовать меньше памяти, чем поле VARCHAR , а ошибки SELECT, связанные с конкретной таблицей, будут быстрее. Дополнительный 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_nameenum соответствующим образом.

альтернатива, что я нахожу несколько хуже в вашем случае будет использовать отдельные поля идентификаторов для каждой главной таблицы:

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; 

Здесь, один из полей идентификаторов будет содержать значение, а другой два будут NULL.Для вставки ошибки, связанную с таблицей сайтов и Индентификационными 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. Однако выбор ошибок для одного конкретного объекта и идентификатора требует только одного сравнения полей, например:

SELECT * FROM error_logs2 WHERE cards_id = 5 
+0

Marcellus, большое спасибо за ваш ввод! Я использую ваше первоначальное предложение и создаю таблицу, как вы указали с типом данных enum для столбца таблицы. Благодарю. – Kate

+0

Добро пожаловать, рад помочь :) – Marcellus

Смежные вопросы