2013-08-28 2 views
0

Фон: в поведенческом эксперименте шмели отмечены уникальным идентификатором, чтобы отслеживать их движение. Проблема в том, что теги имеют только 2 цифры, а колония может достигать 500 человек. Это затрудняет создание первичного ключа.Объединить запросы INSERT и SELECT для получения уникального идентификатора

ТАБЛИЦЫ

(1). Каждый выбор указан в этой таблице:

CREATE TABLE `exp8` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `bee_id` varchar(255) DEFAULT NULL, 
    `date_time` datetime DEFAULT NULL, 
    `choice` varchar(255) DEFAULT NULL, 
    `hover_duration` int(11) DEFAULT NULL, 
    `antennate_duration` int(11) DEFAULT NULL, 
    `land_duration` int(11) DEFAULT NULL, 
    `landing_position` varchar(255) DEFAULT NULL, 
    `remarks` longtext, 
    `validity` int(11) DEFAULT '1', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=264; 

LOCK TABLES `exp8` WRITE; 
/*!40000 ALTER TABLE `exp8` DISABLE KEYS */; 

INSERT INTO `exp8` (`id`, `bee_id`, `date_time`, `choice`, `hover_duration`, `antennate_duration`, `land_duration`, `landing_position`, `remarks`, `validity`) 
VALUES 
    (1,NULL,'2013-05-14 15:38:31','right',1,0,0,NULL,NULL,1), 
    (2,NULL,'2013-05-18 10:27:15','left',1,0,0,NULL,NULL,1), 
    (3,'G5','2013-05-18 11:44:44','left',0,0,4,'yellow',NULL,1), 
    (4,'G5','2013-06-01 10:00:00','left',0,0,4,'yellow',NULL,1); 

(2). Дата начала и окончания тега записывается в эту таблицу:

CREATE TABLE `tags` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `bee_id` varchar(255) DEFAULT NULL, 
    `tag_date` date DEFAULT NULL, 
    `colony_id` int(11) DEFAULT NULL, 
    `events` varchar(255) DEFAULT NULL, 
    `worker_age` varchar(255) DEFAULT NULL, 
    `tagged_by` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=406; 

LOCK TABLES `tags` WRITE; 
/*!40000 ALTER TABLE `tags` DISABLE KEYS */; 

INSERT INTO `tags` (`id`, `bee_id`, `tag_date`, `colony_id`, `events`, `worker_age`, `tagged_by`) 
VALUES 
    (1,'G5','2013-05-08',1,'birth','Adult','ET'), 
    (2,'G5','2013-05-20',NULL,'death','Adult','ET'), 
    (3,'G5','2013-05-29',1,'birth','Adult','ET'); 

(3). Запрос, различать многократное использование тега:

select t.bee_id, 
     (case when t.death_date is null then 'Alive' else 'Dead' end) as status, 
     t.tag_date, 
     t.death_date, 
     (case when t.death_date is not null then timediff(t.death_date,t.tag_date) 
      else timediff(NOW(),t.tag_date) end) as age 
from (select t.*, 
      (select t2.tag_date 
       from tags t2 
       where t2.bee_id = t.bee_id and 
        t2.events = 'death' and 
        t2.tag_date >= t.tag_date 
       limit 1 
      ) as death_date 
     from tags t 
     where t.events = 'birth' 
    ) t 
group by t.bee_id, t.tag_date; 

Есть ли у вас какие-либо советы по генерации первичного ключа на основе этих данных?

Заранее благодарен! Levi

+0

Первичный ключ для чего? Каждый пчела? –

+0

Да, первичный ключ для каждого пчелы, который различает пчел, которые использовали один и тот же тег. – Levi

+3

Я мог бы упростить, но если 'bee_id' является 2-значным кодом тега,' tag_date' - это дата, когда вы помещаете тег на пчелу и предполагаете, что вы не будете повторно использовать тег в тот же день, казалось бы, как первичный ключ с двумя столбцами '(bee_id, tag_date)' будет кандидатом ... это будет 2 байта для кода и 3 байта для даты ... и у вас есть 5-байтовый первичный ключ ... который кажется довольно разумным. Не «генерируйте» ключ, просто «используйте» естественный ключ, который у вас есть. –

ответ

1

Подход к тому, чтобы сделать это более управляемым, зависит от того, сколько рефакторинга вы в настроении. :) Создание ответа на этот вопрос сложно, потому что я стараюсь оставаться рядом с тем, что у вас уже есть, а не предлагать существенный пересмотр.

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

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

Вот некоторые общие замечания.

Для полей 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.

+0

Спасибо за подробный ответ! Решение работоспособно, но я в тупике: ассистенты-исследователи редко используют идентичные идентификаторы тегов на двух живых пчелах. Принцип здесь должен состоять в том, чтобы простить такую ​​ошибку. Я новичок со сложными запросами, но я думаю, что такой запрос модификации может создать несколько записей для нескольких идентификаторов тегов Live: INSERT INTO tags_copy ('bee_id',' birth_date', 'death_date',' tagged_by') \t ВЫБОР \t \t t.bee_id, \t \t t.tag_date, \t \t (SELECT bee_id из тегов t2 WHERE t.bee_id = t2.bee_id и события = 'смерть' ORDER BY tag_date LIMIT 1) death_date, \t \t t.tagged_by \t FROM tags t – Levi

+0

Не могу изменить мой предыдущий комментарий, но «прощающий» запрос, на мой взгляд, следующий: INSERT INTO tags_copy ('bee_id',' birth _date', 'death_date',' tagged_by') \t ВЫБОР \t \t t.bee_id, \t \t t.tag_date, \t \t (SELECT tag_date \t \t \t из тегов t2 \t \t \t ГДЕ t.bee_id = t2.bee_id и события = 'смерть' и t.tag_date <= t2.tag_date \t \t \t ORDER BY tag_date LIMIT 1) AS death_date, \t \t t.tagged_by \t ОТ тегов t \t где события = 'рождение' – Levi

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