SQLITE: проблема тегів та продуктів


10

Я шукаю спосіб створення запиту, щоб зробити наступне:

Розглянемо 3 таблиці:

  • продукція: Список товарів
  • теги: список тегів
  • tag_ties: таблиця, яка використовується для асоціації тегу до продукту

Розглянемо цю структуру для кожної таблиці:

Продукція:

  • id (int, автоматичне збільшення)
  • назва (варчар, назва товару)

Теги:

  • id (int autoincrement)
  • мітка (varchar, ярлик тегу)

Тег_тіси:

  • id (int, автоматичне збільшення)
  • tag_id (int, посилання на ідентифікатор тегу)
  • ref_id (int, посилання на ідентифікатор продукту)

Що я хочу:

Отримайте, наприклад, усі продукти, позначені тегами id 10, 11 та 12.

Цей запит не працює, оскільки він повертає продукти, що мають принаймні один із тегів:

select 
    p.name as name,
    p.id as id
from 
    products p inner join tag_ties ties
on
    p.id=ties.ref_id
where
    ties.ref_id=p.id and
    ties.tag_id in (10,11,12)
group by 
    p.id
order by 
    p.name asc

Відповіді:


9

Спробуйте щось подібне:

select
    t1.id,
    t1.name
from
    (
    select
        p.name as name,
        p.id as id
    from
        products p inner join tag_ties ties
    on
        p.id=ties.ref_id
    where
        ties.tag_id in (10,11,12)
    ) as t1
group by
    t1.id,
    t1.name
having
    count(t1.id) = 3
order by
    t1.name asc
;

Це працює :)
Julien L

11

Вирішити цю проблему можна за допомогою операторів перетину. Зробіть окремий вибір для кожного tag_id та приєднайте їх до перехрестя, і ви отримаєте лише записи, які відповідають усім трьом тегам_id.

select products.id, products.name from 
products join tag_ties
on tag_ties.ref_id = products.id
where tag_ties.tag_id = 10
intersect
select products.id, products.name from 
products join tag_ties
on tag_ties.ref_id = products.id 
where tag_ties.tag_id = 11
intersect
select products.id, products.name from 
products join tag_ties
on tag_ties.ref_id = products.id 
where tag_ties.tag_id = 12

Ось довідкова стаття про використання перехрестя

Ви також можете використовувати тимчасовий вигляд, щоб зробити цей вигляд трохи приємнішим.

create temporary view temp_view as 
select name, products.id as id, tag_ties.tag_id as tag_id 
from products join tag_ties
on tag_ties.ref_id = products.id

select name, id from temp_view where tag_id = 10
intersect ...

8

Підзапит із вибраної відповіді не потрібен. Вибрати продукти з усіма заданими ідентифікаторами тегів запит можна просто:

SELECT 
    p.*
FROM 
    products AS p
INNER JOIN
    tag_ties AS tt
ON
    tt.ref_id = p.id
AND 
    tt.tag_id IN (10, 11, 12)
GROUP BY 
    p.id
HAVING 
    COUNT(p.id)=3

Розширюючи цю ідею, ми також можемо запитувати на основі міток тегів в одному кадрі. Щоб вибрати продукти з тегами ('foo', 'bar', 'baz'):

SELECT 
    p.*
FROM 
    products AS p
INNER JOIN
    tags AS t
ON
    t.label IN ('foo', 'bar', 'baz')
INNER JOIN
    tag_ties AS tt
ON
    tt.ref_id = p.id
AND 
    tt.tag_id = t.id
GROUP BY 
    p.id
HAVING 
    COUNT(p.id)=3

Щоб трохи ускладнити його, ми можемо використовувати підзапит для змішування intersection ( AND) та union ( OR). Поданий запит поверне продукти з усіма тегами групи ('foo', 'bar')та принаймні одним із тегів групи ('baz', 'ding'):

SELECT 
    p.*
FROM 
    (
    SELECT 
        p.*
    FROM 
        products AS p
    INNER JOIN 
        tags AS t
    ON
        t.label IN ('foo', 'bar')
    INNER JOIN 
        tag_ties AS tt
    ON
        tt.ref_id = p.id
    AND 
        tt.tag_id = t.id
    GROUP BY 
        p.id
    HAVING 
        COUNT(p.id)=2
    ) AS p
INNER JOIN 
    tags AS t
ON 
    t.label IN ('baz', 'ding')
INNER JOIN
    tag_ties AS tt
ON
    tt.ref_id = p.id
AND 
    tt.tag_id = t.id
GROUP BY 
    p.id

2
Вам не потрібен JOIN? Ні, технічно ви цього не робите, але чи є причина, щоб не використовувати її? І повертаємось до позначення SQL-89 неявних приєднань?
ypercubeᵀᴹ

5
Я підтримую заявку, тому що ви повинні використовувати ПРИЄДНАЙТЕ завжди. stackoverflow.com/questions/5654278/… Без явного ПРИЄДНУЙСЯ, ваш код не буде розгорнуто в моєму магазині
gbn

3
Хлопці, дякую, що сказали мені, що неявні приєднання - це поганий стиль. Я в основному мав намір зазначити, що підзапис із вибраної відповіді не потрібен. Я відредагував відповідь, щоб використовувати приєднання. Якщо ви бачите щось інше не в моїх запитах, будь ласка, повідомте мене про це.
морени

5
+1 за те, що брати участь у шлюбі, не засмучуючись і фактично враховуючи поради щодо покращення своїх навичок.
Зейн

2
Що сказав @Zane. +1 теж
gbn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.