문제

나는 가장 좋은 방법이 SO에 사용 된 것과 같은 태그 시스템을 구현하는 것이 무엇인지 궁금했습니다. 나는 이것을 생각하고 있었지만 좋은 확장 가능한 솔루션을 찾을 수는 없습니다.

나는 기본 3 테이블 솔루션을 가지고 있다고 생각하고있었습니다. tags 테이블, an articles 테이블과 a tag_to_articles 테이블.

이것이이 문제에 대한 최선의 해결책입니까, 아니면 대안이 있습니까? 이 방법을 사용하여 테이블은 제 시간에 매우 커질 것이며, 검색하기 위해 너무 효율적이지 않습니다. 반면에 쿼리가 빠르게 실행하는 것이 중요하지 않습니다.

도움이 되었습니까?

해결책

이 블로그 게시물을 흥미롭게 찾을 수 있다고 생각합니다. 태그 : 데이터베이스 스키마

문제 : 원하는만큼의 태그로 북마크 (또는 블로그 게시물 등)를 태그 할 수있는 데이터베이스 스키마를 원합니다. 나중에 북마크를 노조 또는 태그 교차로로 제한하기 위해 쿼리를 실행하려고합니다. 또한 검색 결과에서 일부 태그를 제외하고 (예 : 마이너스) 제외하려고합니다.

"mysqlicious"솔루션

이 솔루션에서 스키마에는 단 하나의 테이블 만 가지고 있으며, 비정규 화됩니다. MySQlicious는 Del.icio.us 데이터를이 구조의 테이블로 가져 오기 때문에이 유형은 "mysqlicious 솔루션"이라고합니다.

enter image description hereenter image description here

"Search+Webservice+Semweb"의 교차 (및) 쿼리 :

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags LIKE "%semweb%"

"검색 | 웹 서비스 | SEMWEB"에 대한 Union (OR) 쿼리 :

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
OR tags LIKE "%webservice%"
OR tags LIKE "%semweb%"

"Search+Webservice-Semweb"에 대한 마이너스 쿼리

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags NOT LIKE "%semweb%"

"Scuttle"솔루션

스커틀 데이터를 두 테이블로 구성합니다. 이 테이블 "SCCATEGORIES"는 "태그"-테이블이며 "책갈피"-테이블에 대한 외국 키를 가지고 있습니다.

enter image description here

"책갈피+웹 서비스+semweb"의 교차 (및) 쿼리 :

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId
HAVING COUNT( b.bId )=3

먼저, 태그가 "북마크", "웹 서비스"또는 "semweb"(C.category in ( 'Bookmark', 'Webservice', 'Semweb') 인 모든 북마크 태그 조합이 검색됩니다. 검색 된 세 개의 태그가 모두 고려됩니다 (Count (B.Bid) = 3).

"Bookmark | Webservice | Semweb"에 대한 Union (OR) 쿼리 :조항을 제외하고는 노조가 있습니다.

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId

"Bookmark+Webservice-Semweb"에 대한 마이너스 (제외) 쿼리, 즉 SEMWEB가 아닌 북마크 및 웹 서비스.

SELECT b. *
FROM scBookmarks b, scCategories c
WHERE b.bId = c.bId
AND (c.category IN ('bookmark', 'webservice'))
AND b.bId NOT
IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = 'semweb')
GROUP BY b.bId
HAVING COUNT( b.bId ) =2

카운트를 남기면 "책갈피 | WebService-Semweb"에 대한 쿼리가됩니다.


"Toxi"솔루션

독소 3 개의 테이블 구조를 생각해 냈습니다. 테이블“Tagmap”을 통해 북마크와 태그는 N-to-M 관련입니다. 각 태그는 다른 북마크와 함께 사용할 수 있으며 그 반대도 마찬가지입니다. 이 DB-Schema는 WordPress에서도 사용됩니다. 쿼리는 "Scuttle"솔루션에서와 매우 동일합니다.

enter image description here

"북마크+웹 서비스+SEMWEB"의 교차 (및) 쿼리

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3

"Bookmark | Webservice | Semweb"에 대한 Union (OR) 쿼리

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id

"Bookmark+Webservice-Semweb"에 대한 마이너스 (제외) 쿼리, 즉 SEMWEB가 아닌 북마크 및 웹 서비스.

SELECT b. *
FROM bookmark b, tagmap bt, tag t
WHERE b.id = bt.bookmark_id
AND bt.tag_id = t.tag_id
AND (t.name IN ('Programming', 'Algorithms'))
AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = 'Python')
GROUP BY b.id
HAVING COUNT( b.id ) =2

카운트를 남기면 "책갈피 | WebService-Semweb"에 대한 쿼리가됩니다.

다른 팁

3 테이블 솔루션에는 아무런 문제가 없습니다.

또 다른 옵션은 기사에 적용 할 수있는 태그 수를 제한하고 기사 테이블에 직접 추가하는 것입니다.

DB를 정규화하면 한 테이블에 대한 하드 와이어를하는 것과 마찬가지로 이점과 단점이 있습니다.

둘 다 할 수 없다고 말하는 것은 없습니다. 정보를 반복하는 것은 관계형 DB 패러다임에 위배되지만 목표가 성능이라면 패러다임을 깨뜨려야 할 수도 있습니다.

제안 된 세 가지 테이블 구현은 태깅에 효과가 있습니다.

그러나 스택 오버플로는 다양한 구현을 사용합니다. 태그를 일반 텍스트로 게시물 테이블에 Varchar 열에 저장하고 전체 텍스트 인덱싱을 사용하여 태그와 일치하는 게시물을 가져옵니다. 예를 들어 posts.tags = "algorithm system tagging best-practices". 나는 Jeff가 이것을 어딘가에 언급했다고 확신하지만 어디에서 잊어 버렸습니다.

제안 된 솔루션은 최선의 방법입니다. 태그와 기사 사이의 다수의 관계를 다루기 위해 내가 생각할 수있는 유일한 방법은 아닙니다. 그래서 제 투표는 '그렇습니다. 여전히 최고입니다.' 나는 모든 대안에 관심이 있습니다.

데이터베이스가 인덱스 가능한 배열 (예 : PostgreSQL과 같은)을 지원하는 경우 완전히 비정규 화 솔루션을 권장합니다. 태그를 동일한 테이블의 문자열 배열로 저장합니다. 그렇지 않은 경우, 보조 테이블 매핑 객체가 태그에 대한 최상의 솔루션입니다. 태그에 대한 추가 정보를 저장 해야하는 경우 별도의 태그 테이블을 사용할 수 있지만 모든 태그 조회에 대해 두 번째 조인을 도입 할 필요는 없습니다.

더 나은 성능을 위해 최적화 된 MySQLicious를 제안하고 싶습니다. 그 전에 Toxi (3 표) 솔루션의 단점은 다음과 같습니다.

수백만 개의 질문이 있고 각각에 5 개의 태그가있는 경우 Tagmap 테이블에는 5 백만 개의 항목이 있습니다. 따라서 먼저 태그 검색을 기반으로 10,000 개의 타그 맵 항목을 필터링 한 다음 10 천명의 일치 질문을 다시 필터링해야합니다. 따라서 아프리카 ID가 단순한 숫자 인 경우 필터링하는 동안 괜찮지 만, UUID (32 varchar)의 일종이라면 필터링되면 더 큰 비교가 필요합니다.

내 해결책 :

새 태그가 생성 될 때마다 카운터 ++ (베이스 10)를 갖고 해당 카운터를 Base64로 변환하십시오. 이제 각 태그 이름에는 Base64 ID가 있습니다. 그리고이 ID를 이름과 함께 UI에 전달하십시오. 이렇게하면 시스템에 4095 개의 태그가 생성 될 때까지 최대 2 개의 Char ID를 가질 것입니다. 이제이 다중 태그를 각 질문 테이블 태그 열에 연결하십시오. 구분기를 추가하고 정렬하십시오.

테이블은 다음과 같습니다

enter image description here

쿼리하는 동안 실제 태그 이름 대신 ID에서 쿼리. 그이기 때문에 정렬, and 태그의 상태가 더 효율적입니다 (LIKE '%|a|%|c|%|f|%).

단일 공간 구분 기호는 충분하지 않으며와 같이 태그를 구별하려면 이중 구분 기가 필요합니다. sql 그리고 mysql 왜냐하면 LIKE "%sql%" 돌아올 것입니다 mysql 결과도. 해야한다 LIKE "%|sql|%"

검색이 색인화되지 않았다는 것을 알고 있지만 여전히 author/dateTime과 같은 기사와 관련된 다른 열에서 색인이 표시되었을 수 있습니다.

마지막 으로이 솔루션을 사용하면 백만 레코드를 가입 조건에 대한 5 백만 레코드와 비교 해야하는 내부 조인이 필요하지 않습니다.

CREATE TABLE Tags (
    tag VARHAR(...) NOT NULL,
    bid INT ... NOT NULL,
    PRIMARY KEY(tag, bid),
    INDEX(bid, tag)
)

메모:

  • 이것은 Toxi보다 더 많은 것을 겪지 않는다는 점에서 Toxi보다 낫습니다. 최적화가 어려워지는 많은 테이블이 있습니다.
  • 물론, 내 접근 방식은 중복 태그로 인해 약간 더 부피가 커질 수 있지만 이는 적은 비율입니다. 전부의 데이터베이스 및 성능 개선이 중요 할 수 있습니다.
  • 매우 확장 가능합니다.
  • 대리자가 없기 때문에 (필요하지 않기 때문에) AUTO_INCREMENT PK. 따라서 스쿠틀보다 낫습니다.
  • mysqlicious는 색인을 사용할 수 없기 때문에 짜증납니다 (LIKE ~와 함께 주요한 와일드 카드; 하위 문자열에 대한 허위 히트)
  • MySQL의 경우 '클러스터링'효과를 얻으려면 Engine = InnoDB를 사용해야합니다.

관련 토론 (MySQL 용) :
많은 : 많은 매핑 테이블 최적화
주문 목록

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top