質問
ウェブサイトにタグ付けシステムを実装しています。オブジェクトごとに複数のタグがあり、タグごとに複数のオブジェクトがあります。これは、レコードごとに2つの値を持つオブジェクトを保持することで実現されます。1つはオブジェクトのID用、もう1つはタグ用です。
特定のタグのセットに一致するオブジェクトを見つけるためのクエリを作成しようとしています。次のデータ([オブジェクト]-<!> gt; [タグ] *形式)があったとします
apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red
マッチさせたい場合(赤)、リンゴと消防車を手に入れる必要があります。 (果物、食べ物)を一致させたい場合は、(リンゴ、バナナ)を手に入れる必要があります。
SQLクエリを作成して、必要なことを行うにはどうすればよいですか
@Jeremy Ruten、
ご回答ありがとうございます。使用された表記は、サンプルデータを提供するために使用されました-データベースには、レコードごとに1つのオブジェクトIDと1つのタグを持つテーブルがあります。
第二に、私の問題は、すべてのタグに一致するすべてのオブジェクトを取得する必要があることです。 ORをANDに置き換えるには:
SELECT object WHERE tag = 'fruit' AND tag = 'food';
実行しても結果は得られません。
解決
指定:
- オブジェクトテーブル(プライマリキーID)
- objecttagsテーブル(外部キーobjectId、tagid)
-
tagsテーブル(プライマリキーID)
SELECT distinct o.* from object o join objecttags ot on o.Id = ot.objectid join tags t on ot.tagid = t.id where t.Name = 'fruit' or t.name = 'food';
これはあなたが望むので後方に思えますが、問題は2つのタグが同じ行になく、したがって、1つの行は果物でも食べ物でもないため、anとandは何も生成しません。 タグごとに各オブジェクトの1行を取得するため、このクエリは通常、重複を生成します。
実際に実行したい場合、この場合、たとえばクエリにgroup by
とhaving count = <number of ors>
が必要になります。
SELECT distinct o.name, count(*) as count
from object o join objecttags ot on o.Id = ot.objectid
join tags t on ot.tagid = t.id
where t.Name = 'fruit' or t.name = 'food'
group by o.name
having count = 2;
他のヒント
おっと、元のコメントを誤って解釈した可能性があります。
SQLでこれを行う最も簡単な方法は、3つのテーブルを持つことです。
1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )
その後、データに対して必要な事実上すべての質問を迅速、簡単、効率的に行うことができます(適切にインデックス付けされている場合)。おしゃれにしたい場合は、マルチワードタグも許可できます(エレガントな方法と、それほどエレガントではない方法が考えられます)。
これがあなたの持っているものだと思うので、以下のSQLは動作します:
文字通りの方法:
SELECT obj
FROM object
WHERE EXISTS( SELECT *
FROM tags
WHERE tag = 'fruit'
AND oid = object_id )
AND EXISTS( SELECT *
FROM tags
WHERE tag = 'Apple'
AND oid = object_id )
次のような他の方法もあります:
SELECT oid
FROM tags
WHERE tag = 'Apple'
INTERSECT
SELECT oid
FROM tags
WHERE tag = 'Fruit'
@Kyle:クエリは次のようになります。
SELECT object WHERE tag IN ('fruit', 'food');
クエリは、タグが果物と食品の両方である行を探していましたが、フィールドには同時に両方の値を含めることはできません。
スティーブMの提案とジェレミーの提案を組み合わせると、探しているものを含む単一のレコードが得られます。
select object
from tblTags
where tag = @firstMatch
and (
@secondMatch is null
or
(object in (select object from tblTags where tag = @secondMatch)
)
今、それはあまりうまくスケールしませんが、あなたが探しているものを手に入れるでしょう。コードに大きな影響を与えることなく、N個の一致するアイテムを簡単に取得できるように、これを実行するためのより良い方法があると思いますが、現在は私を免れています。
次のスキーマをお勧めします。
Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID
次のクエリを使用します。
select distinct
objectName
from
ObjectTab ot
join object o
on o.objectID = ot.objectID
join tabs t
on t.tagID = ot.tagID
where
tagName in ('red','fruit')
次のように、テーブルにレコードごとに1つのタグを付けることをお勧めします:
apple -> fruit
apple -> red
apple -> food
banana -> fruit
banana -> yellow
banana -> food
その後、あなただけで
SELECT object WHERE tag = 'fruit' OR tag = 'food';
本当に自分のやり方でやりたい場合は、次のようにすることができます:
SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';