Сведения о вопросе

Htmlщик

14:46, 18th August, 2020

Теги

sql   many-to-many   tagging    

SQL many-to-many соответствие

Просмотров: 404   Ответов: 6

Я внедряю систему тегов для веб-сайта. Существует несколько тегов на объект и несколько объектов на тег. Это достигается путем ведения таблицы с двумя значениями для каждой записи, по одному для идентификаторов объекта и тега.

Я ищу, чтобы написать запрос, чтобы найти объекты, которые соответствуют заданному набору тегов. Предположим, у меня были следующие данные (в формате [object] - > [tags]* )

apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red

Если я хочу соответствовать (красный), я должен получить apple и firetruck. Если я хочу соответствовать (фрукты, еда), я должен получить (яблоко, банан).

Как написать запрос SQL do do what I want?

@Jeremy Рутен,

Спасибо за ваш ответ. Используемая нотация использовалась для предоставления некоторых образцов данных - в моей базе данных есть таблица с 1 идентификатором объекта и 1 тегом на запись.

Во-вторых, моя проблема заключается в том, что мне нужно получить все объекты, соответствующие всем тегам. Подставляя свой OR вместо AND вот так:

SELECT object WHERE tag = 'fruit' AND tag = 'food';

Не дает никаких результатов при запуске.



  Сведения об ответе

SSESION

14:09, 28th August, 2020

Дано:

  • таблица объектов (идентификатор первичного ключа)
  • таблица objecttags (внешние ключи objectId, tagid)
  • таблица тегов (идентификатор первичного ключа)

    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 одиночный ряд не может быть одновременно фруктом и едой. Этот запрос обычно дает дубликаты, потому что вы получите по 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;


  Сведения об ответе

прога

14:15, 6th August, 2020

О боже, возможно, я неправильно истолковал ваш первоначальный комментарий.

Самый простой способ сделать это в SQL - это иметь три таблицы:

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'


  Сведения об ответе

SKY

10:09, 3rd August, 2020

@Kyle: ваш запрос должен быть более похожим:

SELECT object WHERE tag IN ('fruit', 'food');

Ваш запрос искал строки, в которых тег был одновременно fruit AND food, что невозможно, поскольку поле может иметь только одно значение, а не оба одновременно.


  Сведения об ответе

repe

00:07, 18th August, 2020

Объедините предложение Стива м. с предложением Джереми вы получите одну запись с тем что вы ищете:

select object
from tblTags
where tag = @firstMatch
and (
       @secondMatch is null 
       or 
       (object in (select object from tblTags where tag = @secondMatch)
     )

Теперь это не очень хорошо масштабируется, но он получит то, что вы ищете. Я думаю, что есть лучший способ сделать это, чтобы вы могли легко иметь N совпадающих элементов без большого влияния на код, но в настоящее время он ускользает от меня.


  Сведения об ответе

repe

21:06, 1st October, 2020

Я рекомендую следующую схему.

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')


  Сведения об ответе

dumai

16:38, 20th August, 2020

Я бы предложил сделать так, чтобы ваша таблица имела 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 %';


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться