2014-01-25 2 views
0

개인 웹 사이트의 CMS 시스템에서 작업하고 있습니다 (주로 학습 활동으로). Atm 나는 3 개의 테이블을 가지고있다 : 하나는 기사 용이고, 하나는 태그 용이고 다른 하나는 여러 개의 태그를 가질 수있는 조인 테이블이다.고유하지 않은 열이있는 mySQL 테이블에 중복 항목 피하기

세 개의 열로 구성과 내가 문제가 오전 테이블 -

내 문제는 기사가 여러 번 나타날 수 있으며, 태그는 여러 번 나타날 수 있다는 사실에서 유래
article_tags: id (auto_increment), article_id, tag_id 

, 그러나 두 개의 주어진 조합은 한 번만 나타납니다. 즉, 각 기사는 단일 태그에 대한 하나의 참조 만 가져야합니다. 현재는 INSERT ID가 다른 행을 "복제"할 수 있지만 article_id를하고 tag_id의 조합은 동일합니다 :

id , article_id, tag_id 
1  1   1 
2  1   2  
3  2   1  
4  1   1 <- this is wrong 

나는이 조합을 포함하는 레코드 PHP 코드 확인,하지만 난 할 수 가능한 경우 SQL에서 수행하는 것을 선호합니다 (그렇지 않은 경우 또는 PHP를 사용하여 바람직하지 않은 경우). ID가 다르며 고유 한 열을 설정할 수 없기 때문에 INSERT IGNORE 및 ON DUPLICATE와 같은 항목이 작동하지 않습니다.

저는 아주 새로운 mySQL입니다. 그래서 바보 같은 짓을하고 있다면 올바른 방향으로 나를 가리켜주세요.

감사

이라고도

답변

3

테이블 정의를 검토해야합니다.

당신은 할 수 있습니다 (최고에서 최악으로) :

  1. 이 (article_id를하고 tag_id)에 복합 기본 키를 추가하고
  2. 이 (article_id를에 인덱스 (UNIQUE)를 추가 (이전 기본 키) AUTO_INCREMENT를 제거 테이블은 다음과 같이 정의된다, 지금 테이블

아무것도 변경하지 않고 SELECT DISTINCT(article_id, tag_id) FROM ... : 및 tag_id)와 AUTO_INCREMENT 기본 키

  • 는 PHP에서 서로 다른 선택 유지 이 같은 :

    CREATE TABLE IF NOT EXISTS `article_tags` (
        `article_id` int(11) NOT NULL, 
        `tag_id` int(11) NOT NULL, 
        PRIMARY KEY (`article_id`,`tag_id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
    

    :

    CREATE TABLE IF NOT EXISTS `article_tags` (
        `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
        `article_id` int(11) NOT NULL, 
        `tag_id` int(11) NOT NULL, 
        PRIMARY KEY (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
    

    최상의 솔루션 (옵션 1) article_id를하고 tag_id 현재 (AUTO_INCREMENT) 기본 키를 제거하고 열에 기본 키 (복합)를 추가하는 것입니다 그러나 당신이 절대적으로 당신의 AUTO_INCREMENT 기본 키를 유지하려는 경우, 당신의 컬럼에 인덱스 (독특한)를 추가 (2 옵션) :

    CREATE TABLE IF NOT EXISTS `article_tags` (
        `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
        `article_id` int(11) NOT NULL, 
        `tag_id` int(11) NOT NULL, 
        PRIMARY KEY (`id`), 
        UNIQUE KEY `article_id` (`article_id`,`tag_id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
    

    을 어쨌든, 당신이 원하지 않는 경우는 테이블 definitio을 변경하려면 n, 당신은 항상 PHP 쿼리에서 DISTINCT를 사용할 수 있습니다.

    SELECT DISTINCT(article_id, tag_id) FROM article_tags 
    
  • +0

    매우 간결한 대답입니다. SELECT 쿼리에 오버 헤드가 추가되므로 # 3이 최악이라고 올바르게 이해한다면? 그리고 그것은 # 1과 동일하지만 기존의 auto_increment 형태로 여분의 (불필요한?) 열이 있기 때문에 # 2가 더 나쁩니 까? – ProFishChris

    +0

    더 간결하게. 내 편집 된 대답을 참조하십시오 –

    +0

    더 자세한 그래도! 혼란을 가져 미안하지만, 나는 당신이 그들을 구현하는 방법보다는 당신이했던 방식으로 왜 더 많이 관심이 있는지 – ProFishChris

    3

    이러한 대다 관계 테이블은, 종종 테이블을 조인 단지 두 개의 열이 있고, 둘의 합성의 기본 키를 가지고있다.

    article_id 
        tag_id 
        pk = (article_id, tag_id) 
    

    해당 테이블의 정의를 변경하면 해당 문제를 확실히 해결할 수 있습니다.

    복합 키의 열을 어떻게 정렬해야합니까? 응용 프로그램이 조인 테이블의 항목을 찾는 방법에 따라 다릅니다. 항상 article_id로 시작하여 tag_id를 찾으면 article_id를 먼저 키에 넣습니다. DBMS는 키의 첫 번째 열에 대한 값을 임의로 액세스 할 수 있지만 키의 두 번째 (또는 후속) 열의 값을 찾기 위해 인덱스를 스캔해야합니다.

    (tag_id, article_id) 테이블에 두 번째 인덱스를 만들 수 있습니다. 이렇게하면 tag_id를 기반으로 한 빠른 검색이 가능합니다. "두 열을 모두 색인에 넣으려면 왜 귀찮은가요?"라고 물어보십시오. 색인을 덮는 으로 색인을 만듭니다. 커버 인덱스에서 원하는 값을 인덱스에서 직접 검색 할 수 있습니다. 예를 들면, 피복 률과,

    SELECT article_id FROM article_tag WHERE tag_id = 12345 
    

    유일한 결과를 얻기 위해 디스크 드라이브의 인덱스를 액세스해야하는 (또는 그와 유사한 룩업 논리를 사용 JOIN). 커버링 인덱스가없는 경우, 쿼리는 인덱스에서 데이터 테이블로 점프해야하는데 이는 추가 단계입니다.

    조인 테이블은 일반적으로 매우 짧은 행 (정수)을 가지므로 몇 가지 커버 인덱스 (기본 키와 여분의 인덱스)에 대한 복제 된 데이터는 큰 디스크 공간 돼지가 아닙니다.

    +0

    이것은 정확히 내가 찾던 우아한 해결책입니다. 복합 키는 필자가 인정한 제한된 경험에서 만난 것이 아닙니다. 종합 지수의 순서간에 눈에 띄는 차이가 있습니까? 아니면 중요하지 않습니까? – ProFishChris

    +0

    @ProFishChris 내 편집을 참조하십시오. 좋은 질문. –

    +0

    와우는 내가 생각했던 것보다 훨씬 더 복잡했습니다. 정말 흥미로운. 설명해 주셔서 감사합니다 – ProFishChris

    관련 문제