У меня есть product_table
с product_id
среди нескольких других полей.
Я создаю a tag_table
с 2 полями, tag_id
и tag_name
.
Теги могут быть похожими на "серебро" или "состояние penn" и т.д.
Я также создаю таблицу product_tag_map
с product_id
и tag_id
, чтобы сопоставить продукт с любым количеством тегов.
Если бы я хотел создать категорию, содержащую все продукты, помеченные как "серебро", "ожерелье", "бриллиант". Но также исключить любой продукт, помеченный как "начальный", "путешествие"
(Очевидно, что я бы использовал tag_id not tag_name, поэтому продукты с тегами [2,4,5], у которых нет тегов [3,6])
Как создать временную таблицу продуктов и заполнить ее соответствующими продуктами?
вот моя таблица product_tag_map:
CREATE TABLE `product_tag_map` (
`product_tag_map_id` int(11) NOT NULL auto_increment,
`tag_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
PRIMARY KEY (`product_tag_map_id`),
UNIQUE KEY `tag_id` (`tag_id`,`product_id`),
KEY `tag_id_2` (`tag_id`)
) ENGINE=MyISAM AUTO_INCREMENT=7897 DEFAULT CHARSET=utf8
Примечание. Я не использую product_tag_map_id
для чего-либо. Я просто привык давать каждой таблице первичный ключ. Поэтому, если я должен удалить его, это нормально.
Вопрос в том, для чего вам нужна временная таблица? Почему бы не запросить его напрямую?
SELECT
p.product_id,
p.product_name
FROM
product_table p
WHERE
EXISTS (
SELECT 1
FROM product_tag_map
WHERE product_id = p.product_id AND tag_id IN (2,4,5)
)
AND NOT EXISTS (
SELECT 1
FROM product_tag_map
WHERE product_id = p.product_id AND tag_id IN (3,6)
)
Создайте соответствующие индексы (один индекс с несколькими столбцами над (product_tag_map.product_id, product_tag_map.tag_id)
и один отдельный над (product_tag_map.tag_id)
, в дополнение к "нормальным" индексам PK/FK), и это должно быть довольно быстро.
РЕДАКТИРОВАТЬ: Кэшируемая (по сравнению с планами запросов), и более динамичный вариант выше:
Создайте таблицу user_searches
(search_session_id, tag_id, include)
с индексом multi_column над (search_session_id, include)
и отдельным индексом над tag_id
. Затем заполните его, когда пользователь выбирает критерии:
search_session_id tag_id include
...
4711 2 1
4711 4 1
4711 5 1
4711 3 0
4711 6 0
...
И запрос вроде этого:
SELECT
p.product_id,
p.product_name
FROM
product_table p
WHERE
EXISTS (
SELECT 1
FROM product_tag_map m INNER JOIN user_searches s ON s.tag_id = m.tag_id
WHERE m.product_id = p.product_id
AND s.search_session_id = 4711 /* this should be a parameter */
AND s.include = 1
)
AND NOT EXISTS (
SELECT 1
FROM product_tag_map m INNER JOIN user_searches s ON s.tag_id = m.tag_id
WHERE m.product_id = p.product_id
AND s.search_session_id = 4711 /* this should be a parameter */
AND s.include = 0
)
Вы можете создать представление для оператора select.
(Вид - это, в основном, виртуальная таблица, которую вы можете легко запросить, но которая заполняется данными из сложного оператора. Прочтите руководство, его не так просто, когда дело доходит до производительности и поведения чтения/записи.)
Однако ваш запрос может выглядеть так:
SELECT
product_id, count(*) as total
FROM tag_map WHERE tag_id IN (2,4,5)
AND total = 3
AND product_id NOT IN (
SELECT product_id, count(*) as total FROM tag_map WHERE tag_id IN (3,6)
WHERE total = 2 GROUP BY ( product_id )
)
GROUP BY ( product_id )
вы также можете делать соединения, но я думаю, что он будет медленнее.