2013-03-18 2 views
4

Допустим, у меня есть эта таблица:Найти вхождения отмеченных элементов в таблице тузд

item_id tag_id 
------- ------ 
1  1 
1  2 
2  2 
2  3 

Как вы можете себе представить, это таблица, в которой у меня есть ссылки на некоторые предметы и теги, которые принадлежат им. Элемент может иметь более одного тега, и один тег можно выбрать для нескольких элементов.

Предположим, у меня есть также специальный коллектив тегов (f.ex. tag_id = 50, 73 и 119) и таблица «items» с идентификатором (указанным item_id).

Есть ли эффективный запрос, который дает мне:

  1. количество элементов с этими тегами
  2. сами пункты?

То, что я пытался

SELECT COUNT(*) FROM 
(
SELECT COUNT(*) AS c FROM items_tags it JOIN items i ON i.id = it.item_id 
WHERE (tag_id=7 OR tag_id=95 OR tag_id=150) AND `status`='active' 
GROUP BY item_id 
) t1 WHERE c=3 <-- c= number of tags 

я могу иметь оба результата, но с очень (кажется) неэффективный запрос. После экзамена с EXPLAIN я хотел бы избавиться от «диапазона», данного OR.

Уточнение моей проблемы: Проблема в том, что мне была дана очень плохо написанная PHP-фреймворк, которая повторяется более 900 раз через различные идентификаторы тегов. Предположим, у вас есть один или несколько фиксированных идентификаторов (выбранные теги), и он повторяет все теги 900+, чтобы найти количество вхождений элементов, которые имеют общие теги PLUS, итерацию (это функция для уточнения поиск, показывающий только те элементы, у которых есть все теги плюс один).

Данный код работает следующим образом: я выбираю один или несколько тегов и их идентификатор входят в строку запроса. Предположим, я выбрал теги 54 и 77. Код должен найти каждый элемент ID для элементов, которые имеют BOTH теги 54 и 77, и перечислить их один за другим: мы получаем список «элементы с выбранными тегами».

Затем он предлагает выбор для уточнения поиска, и здесь идет нечетная часть: цикл кода PHP обрабатывает ВСЕ теги 900+, и для каждой итерации он принимает тег и подсчитывает, сколько элементов имеет ВСЕ теги 54, 77 и один на итерации. Если счетчик> 0, он отображает имя тега с номером счета, отфильтровывая каждый тег, элементы которого не имеют ссылки на выбранные теги.

Было бы неплохо добиться такого же результата в менее интенсивном режиме.

+0

Какой результат вы ожидаете? –

+0

Вам нужны элементы, которые соответствуют _all_ те теги или _any_ из них? – PinnyM

+0

Все они, спасибо. Вывод, который я ожидаю, в первом случае представляет собой одно число элементов, соответствующих всем тегам, во втором - строки таблицы «items» с совпадением идентификатора item_id, имеющего все теги. – Cranio

ответ

3

Чтобы получить список элементов идентификаторов, которые соответствуют всех тегов, вы можете использовать этот запрос:

SELECT items.id 
FROM items 
JOIN items_tags ON items.id = items_tags.item_id 
WHERE (items_tags.tag_id IN (7,95,150)) 
    AND (items.status = 'active') 
GROUP BY items.id 
HAVING COUNT(DISTINCT items_tags.tag_id) = 3 

Обратите внимание, что если вы уверены, что вы никогда не повторяющиеся тегов для того же элемент, вам может заменить COUNT(DISTINCT items_tags.tag_id) на COUNT(*) для эффективности.

Чтобы получить количество этих элементов, обернуть это в COUNT запроса:

SELECT COUNT(*) 
FROM (
    SELECT items.id 
    ... 
) t 

Чтобы получить список элементов, оберните его в этом запросе на выборку:

SELECT * 
FROM items 
WHERE id IN (
    SELECT items.id ... 
) 

UPDATE

Чтобы получить количество элементов для каждого из остальных тегов в сочетании с исходным списком, вы можете сделать это:

SELECT tag_id, COUNT(DISTINCT item_id) 
FROM items_tags 
WHERE item_id IN (
    SELECT items.id 
    ... 
) 
    AND tag_id NOT IN (7,95,150) 
GROUP BY tag_id 
+0

Является ли DISTINCT необходимым, если он находится в столбце id таблицы? Конечно, предполагается, что id является первичным ключом таблицы. –

+0

@AaronHathaway: Я изменил свой ответ, чтобы отразить, что OP, по-видимому, хочет элементы, соответствующие тегам _all_. Чтобы ответить на ваш вопрос, да, вам понадобится DISTINCT или у вас будет возвращено несколько строк - по одному для каждого тега, связанного с элементом. – PinnyM

+0

Это хороший момент. –

Смежные вопросы