182

次のタグ付け機能をサポートするデータベースをどのように設計しますか。

  • アイテムには多数のタグを付けることができます
  • 特定のタグセットでタグ付けされたすべてのアイテムの検索は高速である必要があります(アイテムにはALLタグが必要であるため、OR検索ではなくAND検索です)
  • アイテムの作成/書き込みは、迅速な検索/読み取りを可能にするために遅くなる可能性があります

理想的には、(少なくとも)n個の指定されたタグのセットでタグ付けされたすべてのアイテムのルックアップは、単一のSQLステートメントを使用して実行する必要があります。検索するタグの数とアイテムのタグの数は不明であり、高い可能性があるため、JOINを使用することは実用的ではありません。

何か案は?


これまでのすべての回答に感謝します。

しかし、私が間違っていなければ、与えられた答えはタグのOR検索を行う方法を示しています。(n個のタグが1つ以上あるすべてのアイテムを選択します)。効率的なAND検索を探しています。(すべてのn個のタグを持つすべてのアイテムを選択します-そしておそらくそれ以上。)

4

11 に答える 11

82

以下は、データベース スキーマのタグ付けに関する優れた記事です。

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

パフォーマンス テストとともに:

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

そこにある結論は、(少なくとも 2005 年の執筆時点では) 非常に貧弱な全文索引付けの特性を持っていた MySQL に非常に特有のものであることに注意してください。

于 2008-09-07T19:17:04.073 に答える
24

ANDing について: 「関係分割」操作を探しているようです。この記事では、関係分割について簡潔かつわかりやすい方法で説明します。

パフォーマンスについて: ビットマップ ベースのアプローチは、直感的に状況に適しているように思えます。ただし、デジグルが示唆するように、ビットマップ インデックス作成を「手動で」実装するのが良い考えであるとは確信していません。新しいタグが追加されるたびに複雑な状況のように聞こえます(?)。ビルトインのインデックス作成システムにより、インデックスのメンテナンスが複雑になる可能性がなくなるため、役に立ちます。さらに、ビットマップ インデックスを提供する DBMS は、クエリ プランを実行するときにそれらを適切に考慮できる必要があります。

于 2008-09-07T18:22:46.497 に答える
15

@Jeff Atwood がリンクしている記事 ( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/ ) が非常に詳細であることを強調したいと思います (3 つの異なるスキーマのメリットについて説明しています)。 AND クエリに対する優れたソリューションであり、これまでに説明したものよりも優れたパフォーマンスを発揮します (つまり、用語ごとに相関サブクエリを使用しません)。コメント欄にもいいこといっぱい。

ps - ここで話題になっているアプローチは、この記事では "Toxi" ソリューションと呼ばれています。

于 2008-11-05T00:40:15.943 に答える
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シーケンスの評価が速くなります。一致するタグの予想数とそれらのいずれか1つに一致する期待に応じて、これは問題のない解決策になる可能性があります。20個のタグに一致し、ランダムなアイテムが15個に一致すると予想される場合、これは依然として重いでしょう。データベース上。

于 2008-09-07T14:39:39.297 に答える
7

Java コンテンツ リポジトリの実装 ( Apache Jackrabbitなど)のような、厳密にはデータベースではないソリューションを試して、その上に構築されたApache Luceneなどの検索エンジンを使用することをお勧めします。

適切なキャッシング メカニズムを備えたこのソリューションは、自家製のソリューションよりも優れたパフォーマンスを発揮する可能性があります。

ただし、小規模または中規模のアプリケーションでは、以前の投稿で言及した正規化されたデータベースよりも高度な実装が必要になるとは思いません。

編集:あなたの明確化により、検索エンジンでJCRのようなソリューションを使用する方が説得力があるようです。これにより、長期的にはプログラムが大幅に簡素化されます。

于 2008-09-07T14:52:22.587 に答える
5

最も簡単な方法は、タグテーブルを作成することです。
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]

UPDATE
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]
于 2008-09-07T14:39:52.523 に答える
1

完全に(R)DB中心ではないものが必要になるかもしれないという@Zizzencsの2番目の提案をしたいと思います

どういうわけか、プレーンな nvarchar フィールドを使用して、そのタグを適切なキャッシング/インデックス付けで保存すると、より高速な結果が得られる可能性があると思います。しかし、それは私だけです。

以前、多対多の関係を表すために 3 つのテーブルを使用してタグ付けシステムを実装しました (Item Tags ItemTags)。しかし、多くの場所でタグを扱うことになると思います。常に同時に操作/クエリを実行すると、コードがより複雑になります。

追加された複雑さに見合う価値があるかどうかを検討することをお勧めします。

于 2008-09-07T17:38:38.070 に答える
0

上記の答えのバリエーションは、タグIDを取得し、それらを並べ替え、^で区切られた文字列として結合し、ハッシュすることです。次に、ハッシュをアイテムに関連付けるだけです。タグの組み合わせごとに、新しいキーが生成されます。AND検索を実行するには、指定されたタグIDを使用してハッシュを再作成し、検索するだけです。アイテムのタグを変更すると、ハッシュが再作成されます。同じタグのセットを持つアイテムは、同じハッシュキーを共有します。

于 2011-01-14T05:17:45.037 に答える
0

私がやりたいのは、生データを表すテーブルをいくつか用意することです。この場合、

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

これは書き込み時間に対して高速に機能し、すべてを正規化したままにしますが、タグごとに、さらに AND するタグごとにテーブルを 2 回結合する必要があるため、読み取りが遅くなることに注意してください。

読み取りを改善するための解決策は、フラット化された形式でデータを表す新しいテーブルを本質的に作成するストアド プロシージャを設定することにより、コマンドでキャッシュ テーブルを作成することです...

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

次に、挿入ごとにタグ付きアイテム テーブルを最新の状態に保つ必要がある頻度を検討し、カーソル挿入イベントでストアド プロシージャを呼び出します。1 時間ごとのタスクの場合は、1 時間ごとのジョブを設定して実行します。

ここで、データ取得をより巧みに行うために、タグからデータを取得するためのストアド プロシージャを作成する必要があります。大規模な case ステートメントでネストされたクエリを使用するのではなく、データベースから選択するタグのリストを含む単一のパラメーターを渡し、Items のレコード セットを返します。これは、ビットごとの演算子を使用して、バイナリ形式で最適です。

バイナリ形式で、簡単に説明できます。アイテムに割り当てるタグが 4 つあるとします。バイナリで表すと、

0000

4 つのタグすべてがオブジェクトに割り当てられている場合、オブジェクトは次のようになります...

1111

最初の2つだけなら…

1100

次に、必要な列に 1 と 0 を含むバイナリ値を見つけるだけです。SQL Server の Bitwise 演算子を使用すると、非常に単純なクエリを使用して、最初の列に 1 があることを確認できます。

詳細については、このリンクを参照してください

于 2008-09-07T16:25:50.710 に答える
0

結合を避けることはできず、それでもある程度正規化されます。

私のアプローチは、タグ テーブルを持つことです。

 TagId (PK)| TagName (Indexed)

次に、項目テーブルに TagXREFID 列があります。

この TagXREFID 列は 3 番目のテーブルへの 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 などを追加し、動的にクエリを作成します。

于 2008-09-07T14:43:30.113 に答える
0

他の人が言ったことを言い換えると、トリックはschemaではなく、queryにあります。

エンティティ/ラベル/タグの単純なスキーマは正しい方法です。しかし、これまで見てきたように、多数のタグを使用して AND クエリを実行する方法はすぐにはわかりません。

そのクエリを最適化する最善の方法はプラットフォームに依存するため、質問に RDBS でタグを付け直し、タイトルを「タグ付けデータベースで AND クエリを実行する最適な方法」のようなものに変更することをお勧めします。

MS SQL についていくつか提案がありますが、使用しているプラ​​ットフォームが異なる場合は控えます。

于 2008-09-07T17:12:54.233 に答える