Подход к тому, чтобы сделать это более управляемым, зависит от того, сколько рефакторинга вы в настроении. :) Создание ответа на этот вопрос сложно, потому что я стараюсь оставаться рядом с тем, что у вас уже есть, а не предлагать существенный пересмотр.
Это не совсем общий вопрос, но я думаю, что некоторая информация ниже может быть более широко применима. Некоторые из них отражают методы, которые я использую довольно часто, чтобы хранить больше информации в базе данных и вне приложения, использующего базу данных.
Существующие структуры, поскольку они существуют, не обеспечивают чистый способ сохранения реляционной целостности, потому что нет базовой таблицы «помеченных пчел». Таблица тегов, из того, что я могу сказать, больше похожа на таблицу событий пометки пчел. При первом чтении я думал, что это таблица, в которой каждая строка представляет собой отдельную пчелу, но похоже, что каждая пчела может быть представлена двумя строками (возможно, больше, поскольку структуры не дают понять, какие возможные значения данных могут быть .)
Вот некоторые общие замечания.
Для полей VARCHAR(255)
(красный флаг!) Взгляните на тип данных MySQL ENUM
. Существует несколько столбцов, которые, как представляется, поддерживают только небольшой набор возможных допустимых значений. Один из примеров:
events VARCHAR(255) DEFAULT NULL, /* replace this */
events ENUM('birth','death') NOT NULL, /* with this */
ENUM
колонна, как с таблицей поиска, без таблицы, и это хорошо для столбцов, где существует только небольшое число возможных допустимых значений. Вы не можете ввести недопустимое значение в столбце ENUM
, а таблица будет меньше, потому что столбец ENUM
обычно требует только одного байта данных в строке для хранения того, какое значение было введено.
В ваших таблицах нет indexes. Когда набор данных мал, вы можете не заметить разницу, но по мере роста набора данных соответствующие индексы будут иметь резкую разницу в производительности.
Что касается существа вопроса, «настоящая проблема» представляется не просто одним из способов выбора первичного ключа, но и тем, как можно обеспечить целостность данных, чтобы ваш последующий анализ на основе собранных данных выиграл Не будь неточным.
Обратите внимание, что, например, ваш внутренний подзапрос в (3) не является детерминированным:
t2.tag_date >= t.tag_date limit 1
Это не требует базы данных для самой низкой tag_date от t2, которое больше, чем tag_date от внешнего запроса , он запрашивает «не более 1 записи» и работает корректно, только если база данных вернет правильную запись, что часто бывает, но отнюдь не обязательно. База данных может возвращать любую действительную запись в ответ на такой запрос, и вы не должны зависеть от нее, всегда делая то, что она может делать сейчас. Это было бы более правильно написано:
t2.tag_date >= t.tag_date ORDER BY t2.tag_date limit 1
(. Запрос, в (3) является также сложно понять, потому что вы повторно использовать «T» псевдоним означает две разные вещи)
Если я понимаю, правильно, когда вы входите в наблюдения (таблица exp8), ваша забота заключается в том, что наблюдение должно быть связано с правильной пчелой, в идеале без необходимости искать пчелу в таблице «тег».
Если таблица «тег» имеет только события рождения и смерти, вы можете переделать это как таблицу, где каждая строка представляет собой один тег на одной пчеле. Добавьте столбцы birth_date и death_date в таблицу, а затем используйте идентификатор из тега в качестве первичного ключа, удалив «bee_id» и вставив «tag_id» в exp8, тег привязки внешнего ключа (id).
Главный ключ, который вы искали.
Затем вы можете отказаться от запроса в (3) и получить информацию о пчелах, связанную с наблюдениями, простым соединением между tag_id и exp8 на tag.id = exp8.tag_id.
Создайте функцию для поиска по дате и тегу. Это принимает код тега и дату наблюдения и использует его для поиска идентификатора пчелы в таблице (переработанной) тегов.
DELIMITER $$
DROP FUNCTION IF EXISTS find_tag_id $$
CREATE FUNCTION find_tag_id (in_tag VARCHAR(2), in_event_date DATETIME) RETURNS int
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE new_tag_id INT DEFAULT NULL;
SET new_tag_id = (SELECT id FROM tag
WHERE bee_id = in_tag
AND birth_date <= in_event_date
AND (death_date IS NULL OR death_date >= DATE(in_event_date));
IF new_tag_id IS NULL THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'no such bee'; /* force an error */
END IF;
RETURN new_tag_id;
END $$
DELIMITER ;
SELECT find_tag_id('2 digit code','some datetime');
возвратит соответствующий идентификатор из таблицы тегов или выдаст ошибку, если не один.
Затем, вы можете встроить эту функцию прямо внутри вставки запроса, который выглядит следующим образом ...
INSERT INTO `exp8` (`id`, `tag_id`, `date_time`, `choice` ...
VALUES (3,find_tag_id('G5','2013-05-18 11:44:44'),'2013-05-18 11:44:44','left' ...
Значение tag_id будет возвращаемое значение функции, а функция будет бросаться если нет действительной пчелы с этим тегом в эту дату. Он также выдает ошибку Subquery returns more than one row
, если в таблице тегов есть неоднозначные данные, указывающие на то, что на момент наблюдения было больше одного пчелы с тем же тегом.
Вы можете пойти дальше, предполагая измененную таблицу тегов с датами рождения и смерти, а также наложить некоторое здравомыслие на диапазоны дат в таблице тегов с помощью триггеров.
DELIMITER $$
DROP TRIGGER IF EXISTS tag_bi $$
CREATE TRIGGER tag_bi BEFORE INSERT ON tag FOR EACH ROW
BEGIN
IF EXISTS (SELECT * FROM tag WHERE bee_id = NEW.bee_id AND death_date IS NULL) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'there is already a living bee with the specified bee_id';
END IF;
END $$
DELIMITER ;
Существует много больше, чем вы могли бы сделать для обеспечения целостности - большая часть моей работы в качестве администратора базы данных включает в себя сохранение плохих данных из базы данных - но этот триггер дает пример тех вещей, что возможно. Если уже есть пчела с нулевым death_date с тем же самым тегом, который вы пытаетесь вставить, это приведет к тому, что эта таблица будет несовместима с самим собой в отношении личности пчелы с этим тегом в эту дату; триггер заблокирует вставку сообщением об ошибке.A BEFORE UPDATE
триггер может предотвратить неправильные модификации, например, изменить death_date на пчелу на дату после рождения_создания существующей пчелы с тем же тегом.
Надеюсь, это дает некоторые полезные указания. Для этого кода требуется минимум MySQL Server 5.5, так что с тобой все в порядке с 5.6.12.
Первичный ключ для чего? Каждый пчела? –
Да, первичный ключ для каждого пчелы, который различает пчел, которые использовали один и тот же тег. – Levi
Я мог бы упростить, но если 'bee_id' является 2-значным кодом тега,' tag_date' - это дата, когда вы помещаете тег на пчелу и предполагаете, что вы не будете повторно использовать тег в тот же день, казалось бы, как первичный ключ с двумя столбцами '(bee_id, tag_date)' будет кандидатом ... это будет 2 байта для кода и 3 байта для даты ... и у вас есть 5-байтовый первичный ключ ... который кажется довольно разумным. Не «генерируйте» ключ, просто «используйте» естественный ключ, который у вас есть. –