태깅을위한 데이터베이스 디자인


171

다음과 같은 태깅 기능을 지원하기 위해 데이터베이스를 어떻게 설계 하시겠습니까?

  • 항목에 많은 수의 태그가있을 수 있습니다
  • 지정된 태그 세트로 태그가 지정된 모든 항목을 빠르게 검색해야합니다 (항목에 모든 태그가 있어야하므로 OR 검색이 아닌 AND 검색 임)
  • 빠른 검색 / 읽기를 활성화하기 위해 항목 작성 / 쓰기 속도가 느려질 수 있습니다.

이상적으로는 (적어도) n 개의 지정된 태그 세트로 태그가 지정된 모든 항목의 조회는 단일 SQL 문을 사용하여 수행해야합니다. 검색 할 태그의 수와 모든 항목의 태그 수는 알 수없고 높을 수 있으므로 JOIN을 사용하는 것은 실용적이지 않습니다.

어떤 아이디어?


지금까지 모든 답변에 감사드립니다.

그러나 내가 실수하지 않으면 주어진 답변은 태그에 대한 OR 검색을 수행하는 방법을 보여줍니다. 하나 이상의 n 태그가있는 모든 항목을 선택하십시오. 효율적인 AND 검색을 찾고 있습니다. (모두 n 개 이상의 태그가있는 항목을 모두 선택하십시오.)

답변:


22

ANDing 정보 : "관계 구분"작업을 찾고있는 것 같습니다. 이 기사 는 간결하면서도 이해하기 쉬운 관계 구분을 다룹니다.

성능 정보 : 비트 맵 기반 접근 방식은 상황에 가장 적합한 것처럼 직관적으로 들립니다. 그러나 digiguru가 제안한 것처럼 비트 맵 인덱싱을 "수동으로"구현하는 것이 좋은 생각이라고 확신하지 못합니다. 새로운 태그가 추가 될 때마다 복잡한 상황처럼 들립니다 (?) 그러나 일부 DBMS (Oracle 포함)는 어떻게 든 비트 맵 인덱스를 제공합니다 내장 인덱싱 시스템은 인덱스 유지 관리의 복잡성을 없애기 때문에 사용 중입니다. 또한 비트 맵 인덱스를 제공하는 DBMS는 쿼리 계획을 수행 할 때 비트 맵 인덱스를 적절하게 고려할 수 있어야합니다.


4
비트 필드 유형의 데이터베이스를 사용하면 특정 비트 수로 제한되기 때문에 대답은 조금 짧습니다. 즉, 각 항목이 특정 수의 태그로 제한되는 것은 아니지만 전체 시스템에 특정 수의 고유 태그 만있을 수 있습니다 (일반적으로 최대 32 또는 64).
Mark Renouf

1
Question_has_Tag의 Tag_id에서 3nf 구현 (질문, 태그, Question_has_Tag) 및 비트 맵 인덱스를 가정하면 질문에 태그가 추가되거나 제거 될 때마다 비트 맵 인덱스를 다시 작성해야합니다. select * from question q inner join question_has_tag qt where tag_id in (select tag_id from tags where (what we want) minus select tag_id from tags where (what we don't)중간 테이블에 올바른 b- 트리 인덱스가 있다고 가정하면 쿼리 가 정확하고 확장되어야합니다.
Adam Musch

"이 기사"링크가 종료되었습니다. 나는 그것을 읽고 싶었을 것이다 :(
mpen

3
Mark : 이것도 좋아 보입니다 : simple-talk.com/sql/t-sql-programming/… 아마도 내가 언급 한 버전의 재 게시 된 버전 일 것입니다.
Troels Arvin

기사의 URL은 더 이상 유효하지 않습니다
Sebastien H.

77

다음은 데이터베이스 스키마 태그 지정에 대한 좋은 기사입니다.

http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/

성능 테스트와 함께 :

http://howto.philippkeller.com/2005/06/19/Tagsystems-performance-tests/

MySQL에 대한 결론은 (최소한 작성 당시 2005 년에) 전체 텍스트 인덱싱 특성이 매우 낮다는 결론을 내 렸습니다.


1
또한 태그 시스템을 어떻게 구현했는지에 대한 자세한 기술 정보가 필요하십니까? Podcast에서 모든 질문과 함께 모든 태그를 열에 유지 한 다음 즉시 직렬화 / 직렬화 해제한다고 말했습니까? 나는 그것에 대해 더 알고 싶어하고 코드 스 니펫을 볼 수도 있습니다. 주변을 둘러보고 자세한 내용을 찾았습니다. 메타에 대해 질문하기 전에 이미 수행 한 링크가 있습니까?
Marston A.

5
Meta에 대한이 질문에는 SO 스키마에 대한 정보가 있습니다. meta.stackexchange.com/questions/1863/so-database-schema
Barrett

원래 링크는 죽었지 만 새 위치를 찾았습니다. 참조한 기사인지 확인하고 싶을 수 있습니다.
Brad Larson

12
@Jeff가 작성 했음에도 불구하고 이것은 여전히 ​​본질적으로 링크 전용 답변입니다.
curiousdannii

13

간단한 솔루션에는 문제가 없습니다. 항목 용 테이블, 태그 용 테이블, "태깅"을위한 크로스 테이블

크로스 테이블의 인덱스는 충분히 최적화되어야합니다. 적절한 항목을 선택하면

SELECT * FROM items WHERE id IN  
    (SELECT DISTINCT item_id FROM item_tag WHERE  
    tag_id = tag1 OR tag_id = tag2 OR ...)  

AND 태깅은

SELECT * FROM items WHERE  
    EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag1)  
    AND EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag2)  
    AND ...

많은 수의 비교 태그에는 그렇게 효율적이지 않습니다. 메모리에서 태그 수를 유지해야하는 경우 자주 그렇지 않은 태그로 시작하도록 쿼리를 만들 수 있으므로 AND 시퀀스가 ​​더 빨리 평가됩니다. 일치하는 예상 태그 수와 단일 태그 일치에 대한 기대에 따라 20 개의 태그를 일치시키고 임의의 임의 항목이 15 개와 일치 할 것으로 예상하면 괜찮은 해결책이 될 수 있습니다. 데이터베이스에서.


13

@Jeff Atwood가 링크 한 기사 ( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/ )에 가 매우 철저 그리고 지금까지 언급 한 것보다 일반적으로 더 나은 성능을 제공하는 AND 쿼리에 대한 좋은 솔루션을 제공합니다 (즉, 각 용어에 대해 상관 된 하위 쿼리를 사용하지 않음). 또한 의견에 좋은 것들이 많이 있습니다.

추신-여기에 모든 사람들이 이야기하는 접근 방식을 기사에서 "Toxi"솔루션이라고합니다.


3
나는 그 위대한 기사를 읽은 것을 기억하지만 불행히도 그 링크는 이제 죽었습니다. :( 거울을 아는 사람이 있습니까?
localhost

5
링크가 죽었다 : <
Aaron

6

Java 컨텐츠 리포지토리 구현 (예 : Apache Jackrabbit )과 같은 엄격하지 않은 데이터베이스 솔루션을 실험하고 Apache Lucene 과 같은 기반으로 구축 된 검색 엔진을 사용할 수 있습니다 .

적절한 캐싱 메커니즘을 갖춘이 솔루션은 자체 개발 솔루션보다 더 나은 성능을 제공 할 수 있습니다.

그러나 중소 규모의 응용 프로그램에서는 이전 게시물에서 언급 한 표준화 된 데이터베이스보다보다 정교한 구현이 필요하다고 생각하지 않습니다.

편집 : 명확하게하면 검색 엔진에 JCR과 같은 솔루션을 사용하는 것이 더 매력적입니다. 그것은 장기적으로 프로그램을 크게 단순화시킬 것입니다.


5

가장 쉬운 방법은 tags 테이블 을 만드는 것 입니다.
Target_Type-여러 테이블에 태그를 지정하는 경우
Target-태그가 지정된 레코드의 키
Tag - 의 텍스트

데이터 쿼리는 다음과 같습니다.

Select distinct target from tags   
where tag in ([your list of tags to search for here])  
and target_type = [the table you're searching]

업데이트
AND 조건에 대한 요구 사항에 따라 위의 쿼리는 다음과 같이 나타납니다.

select target
from (
  select target, count(*) cnt 
  from tags   
  where tag in ([your list of tags to search for here])
    and target_type = [the table you're searching]
)
where cnt = [number of tags being searched]

1

나는 (Z) DB 중심이 아닌 것을 원할지도 모른다는 @Zizzencs의 두 번째 제안

어떻게 든 일반 nvarchar 필드를 사용하여 적절한 캐싱 / 인덱싱으로 태그를 저장하면 더 빠른 결과를 얻을 수 있다고 생각합니다. 그러나 그것은 단지 나입니다.

이전에 다 대 다 관계를 나타 내기 위해 3 개의 테이블을 사용하여 태깅 시스템을 구현했지만 (Item Tags ItemTags) 많은 장소에서 태그를 처리한다고 가정 할 때 3 개의 테이블을 사용하여 항상 동시에 조작 / 조회되는 것은 코드를 더욱 복잡하게 만듭니다.

추가 된 복잡성이 가치가 있는지 고려할 수 있습니다.


0

조인을 피할 수없고 여전히 정규화 될 수 없습니다.

내 접근 방식은 태그 테이블을 갖는 것입니다.

 TagId (PK)| TagName (Indexed)

그런 다음 items 테이블에 TagXREFID 열이 있습니다.

이 TagXREFID 열은 세 번째 테이블에 대한 FK이므로 TagXREF라고합니다.

 TagXrefID | ItemID | TagId

따라서 항목의 모든 태그를 얻는 것은 다음과 같습니다.

SELECT Tags.TagId,Tags.TagName 
     FROM Tags,TagXref 
     WHERE TagXref.TagId = Tags.TagId 
         AND TagXref.ItemID = @ItemID

태그에 대한 모든 항목을 얻으려면 다음과 같이 사용하십시오.

SELECT * FROM Items, TagXref
     WHERE TagXref.TagId IN 
          ( SELECT Tags.TagId FROM Tags
                WHERE Tags.TagName = @TagName; )
     AND Items.ItemId = TagXref.ItemId;

여러 태그를 함께 AND하기 위해 위의 명령문을 약간 수정하여 AND Tags.TagName = @ TagName1 AND Tags.TagName = @ TagName2 등을 추가하고 쿼리를 동적으로 작성합니다.


0

내가하고 싶은 것은 원시 데이터를 나타내는 많은 테이블이 있기 때문에이 경우에는

Items (ID pk, Name, <properties>)
Tags (ID pk, Name)
TagItems (TagID fk, ItemID fk)

이것은 쓰기 시간 동안 빠르게 작동하고 모든 것을 표준화 된 상태로 유지하지만 각 태그마다 원하는 추가 태그마다 테이블을 두 번 조인해야하므로 읽기 속도가 느립니다.

읽기를 향상시키는 해결책은 기본적으로 데이터를 플랫 형식으로 나타내는 새 테이블을 작성하는 저장 프로 시저를 설정하여 명령에 따라 캐싱 테이블을 작성하는 것입니다.

CachedTagItems(ID, Name, <properties>, tag1, tag2, ... tagN)

그런 다음 태그가있는 항목 테이블을 얼마나 자주 최신 상태로 유지해야하는지 고려할 수 있습니다 (매번 삽입 할 때마다) 커서 삽입 이벤트에서 저장 프로 시저를 호출하십시오. 시간별 작업 인 경우 시간별 작업을 설정하여 실행하십시오.

이제 데이터 검색을 영리하게하려면 태그에서 데이터를 가져 오는 저장 프로 시저를 만들어야합니다. 대규모 case 문에 중첩 쿼리를 사용하는 대신 데이터베이스에서 선택하려는 태그 목록이 포함 된 단일 매개 변수를 전달하고 Items 레코드 집합을 반환하려고합니다. 비트 연산자를 사용하면 이진 형식으로 사용하는 것이 가장 좋습니다.

이진 형식으로 설명하기 쉽습니다. 항목에 할당 할 태그가 4 개 있다고 가정하면 바이너리로 표시 할 수 있습니다.

0000

4 개의 태그가 모두 객체에 할당되면 객체는 다음과 같습니다.

1111

처음 두 개만

1100

그런 다음 원하는 열에서 1과 0으로 이진 값을 찾는 경우입니다. SQL Server의 비트 단위 연산자를 사용하면 매우 간단한 쿼리를 사용하여 첫 번째 열에 1이 있는지 확인할 수 있습니다.

알이 링크를 확인 .


0

말한 다른 사람 의역 : 트릭이 아닌 스키마 , 그것은에서의 쿼리 .

엔티티 / 라벨 / 태그의 순진한 스키마가 올바른 방법입니다. 그러나 앞에서 본 것처럼 많은 태그를 사용하여 AND 쿼리를 수행하는 방법은 즉시 명확하지 않습니다.

이 쿼리를 최적화하는 가장 좋은 방법은 플랫폼에 따라 다르므로 RDBS로 질문에 태그를 다시 지정하고 제목을 "태깅 데이터베이스에서 AND 쿼리를 수행하는 최적의 방법"과 같은 제목으로 변경하는 것이 좋습니다.

MS SQL에 대한 몇 가지 제안이 있지만 사용중인 플랫폼이 아닌 경우에는 삼가십시오.


6
이 문제 영역에서 일하려는 다른 사람들이 실제로 그 기술을 사용하고 이익을 얻을 수 있기 때문에 특정 기술에 대한 약점을주지 마십시오.
Bryan Rehbein

0

위의 답변에 대한 변형은 태그 ID를 가져 와서 정렬하고 ^ 분리 된 문자열로 결합하여 해시하는 것입니다. 그런 다음 해시를 항목에 연결하면됩니다. 태그의 각 조합은 새로운 키를 생성합니다. AND 검색을 수행하려면 제공된 태그 ID와 검색으로 해시를 다시 작성하십시오. 항목의 태그를 변경하면 해시가 다시 만들어집니다. 동일한 태그 세트를 가진 항목은 동일한 해시 키를 공유합니다.


4
이 방법을 사용하면 정확히 동일한 태그 세트를 가진 항목 만 검색 할 수 있습니다. 이는 항상 사소한 일입니다. 내 원래 질문에서 내가 쿼리하는 모든 태그가있는 항목을 찾고 싶습니다.
Christian Berg

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.