2013-08-28 3 views
0

배경 : 행동 실험에서 범블 비브는 고유 한 식별자로 태그가 지정되어 이동을 추적합니다. 문제는 태그가 2 자릿수 인 반면 식민지는 500 명의 개인이 될 수 있다는 것입니다. 이로 인해 기본 키를 생성하는 것이 어려워집니다.INSERT 및 SELECT 쿼리를 결합하여 고유 한 ID를 생성하십시오.

테이블

(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 컬럼 기본 키가 후보가 될 것입니다 ... 그 코드는 2 바이트이고 날짜는 3 바이트가됩니다. 그리고 당신은 5 바이트 기본 키를 가지고 있습니다 ... 꽤 합리적인 것 같습니다. 열쇠를 "생성"하지 말고, 가지고있는 자연 열쇠 만 "사용"하십시오. –

답변

1

더 쉽게 관리 할 수있는 방법은 사용자가 원하는 리팩토링의 정도에 따라 다릅니다. :)이 질문에 대한 답변을 작성하는 것은 까다로운 작업입니다. 왜냐하면 나는 현저한 점검을 제안하기보다는 이미 갖고있는 것과 가깝게 머물러 있기 때문에 어렵습니다.

이것은 일반적인 관심사는 아니지만 아래 정보 중 일부는 더 널리 적용될 수 있습니다. 이 중 일부는 데이터베이스에서 정보를 더 많이 사용하고 데이터베이스를 사용하는 응용 프로그램에서 정보를 유지하기 위해 자주 사용하는 기술을 반영합니다.

"태그가 붙은 꿀벌"의 기본 테이블이 없으므로 현재 구조가 존재하기 때문에 관계형 무결성을 유지하기위한 깨끗한 방법을 제공하지 않습니다. 내가 알 수있는 태그 테이블은 꿀벌 태깅 이벤트의 테이블과 비슷합니다. 첫 번째 읽기에서는 각 행이 개별 꿀벌이었던 테이블이라고 생각했지만 각각의 꿀벌이 2 행으로 표현 될 수있는 것처럼 보였습니다 (가능성이있는 데이터 값이 무엇인지 구조가 명확하게 나타내지 않았기 때문에 더 많을 수도 있음). .)

다음은 몇 가지 일반적인 관찰입니다.

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 열을 테이블에 추가 한 다음 태그의 id를 기본 키로 사용하고 "bee_id"를 제거하고 태그 (id)를 참조하는 외래 키인 exp8에 "tag_id"를 삽입합니다.

찾고있는 기본 키가 있습니다.

그런 다음 (3)에서 쿼리를 생략하고 tag.id = exp8.tag_id에서 tag_id와 exp8 간 간단한 조인으로 관측과 관련된 정보를 얻을 수 있습니다.

날짜와 태그를 기반으로 조회를 수행하는 함수를 만듭니다. 태그 코드와 관측 날짜를 가져 와서 (다시 디자인 된) 태그 테이블에서 벌의 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'); 태그 테이블에서 일치하는 ID를 반환하거나 하나가없는 경우 오류가 발생합니다.

그런 다음 tag_id의 값이 함수의 반환 값이됩니다 ...

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' ... 

바로 다음과 같다하여 삽입 쿼리 내부에이 기능을 포함 할 수 있고, 함수가 발생합니다 그 날에 그 태그를 가진 유효한 벌이 발견되지 않으면 오류. 또한 관찰 당시 동일한 태그로 하나 이상의 벌이 살아 있었다는 것을 모호한 데이터가 태그 테이블에있는 경우 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 ; 

는 무결성을 시행 할 수있는 것보다 더 많은입니다 - DBA가로서 내 작품의 대부분을 포함 데이터베이스에서 잘못된 데이터를 유지 -하지만이 트리거는 당신에게 사물의 종류의 예를 보여줍니다 가능합니다. 이미 삽입하려는 태그와 함께 null death_date가있는 벌이 있다면 해당 날짜에 해당 태그가있는 벌의 신원과 관련하여이 테이블과 일치하지 않게됩니다. 트리거는 오류 메시지와 함께 삽입을 차단합니다.A BEFORE UPDATE 트리거는 부적절한 수정을 방지 할 수 있습니다. 예를 들어 벌에 대한 death_date를 동일한 태그가있는 기존 벌의 birth_date 이후의 날짜로 변경합니다.

유용한 정보가 있으면 좋겠습니다. 이 코드에는 최소한의 MySQL Server 5.5가 필요하기 때문에 5.6.12를 사용할 수 있습니다.

+0

철저한 답변 주셔서 감사합니다! 이 솔루션은 실행 가능하지만 나는 혼란 스럽다. 연구 보조원은 2 개의 라이브 꿀벌에 동일한 태그 ID를 거의 사용하지 않는다. 여기에있는 원칙은 이런 종류의 실수를 용서하는 것입니다. 복잡한 쿼리를 사용하는 초보자이지만이 태그와 같은 수정 쿼리는 여러 개의 라이브 태그 ID에 대해 여러 레코드를 만들 수 있다고 생각합니다. 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 이 death_date AS, \t \t (태그 T2 t.bee_id = t2.bee_id 및 이벤트 = tag_date의 LIMIT 1 BY '죽음'ORDER FROM bee_id SELECT)를 선택 t.tagged_by \t FROM tags t – Levi

+0

나의 이전 코멘트를 편집 할 수는 없지만 이것을 생각해보십시오 : INSERT INTO tags_copy ('bee_id','birth _date''death_date''tagged_by')는 \t는 \t \t 는 (태그 T2 \t \t \t t.bee_id = FROM tag_date \t \t \t을 선택 \t \t t.bee_id, \t \t t.tag_date을 선택 t2.bee_id 및 이벤트 = '죽음'과 t.tag_date <= t2.tag_date \t \t \t ORDER death_date AS tag_date의 LIMIT 1) BY, \t \t t.tagged_by \t 발신 태그 t \t 여기서 events = 'birth' – Levi