Метод, который вы предложили, является, вероятно, наиболее распространенным способом выполнения запроса, но может быть не самым быстрым. Использование объединений может быть быстрее:
SELECT T1.item_id
FROM item2tag T1
JOIN item2tag T2 ON T1.item_id = T2.item_id
JOIN item2tag T3 ON T2.item_id = T3.item_id
WHERE T1.tag_id = 1 AND T2.tag_id = 2 AND T3.tag_id = 3
Вы должны убедиться, что у вас есть следующие индексы:
- Первичный ключ включен (item_id, tag_id)
- Индекс включен (tag_id).
Я проверил производительность этого запроса на соответствие оригиналу в нескольких различных сценариях.
- В случае, когда почти каждый элемент в таблице помечен хотя бы одним из искомых тегов, исходный запрос занимает около 5 секунд, а версия JOIN - около 10 секунд - немного медленнее.
- В случае, когда два из тегов встречаются очень часто, а один из тегов встречается очень редко, исходный запрос занимает около 0,9 секунды, тогда как запрос JOIN занимает всего 0,003 секунды - значительное улучшение производительности.
SQL, который я использовал для теста производительности, вставлен ниже. Вы можете запустить этот тест самостоятельно или немного изменить его и протестировать другие запросы или другие сценарии.
Предупреждение : Не запускайте этот сценарий в производственной базе данных, так как он изменяет содержимое таблицы item2tag
. Запуск сценария может занять несколько минут, поскольку он создает много данных.
CREATE TABLE filler (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;
DELIMITER $$
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
DECLARE _cnt INT;
SET _cnt = 1;
WHILE _cnt <= cnt DO
INSERT
INTO filler
SELECT _cnt;
SET _cnt = _cnt + 1;
END WHILE;
END
$$
CALL prc_filler(1000000);
CREATE TABLE item2tag (
item_id INT NOT NULL,
tag_id INT NOT NULL,
count INT NOT NULL
);
INSERT INTO item2tag (item_id, tag_id, count)
SELECT id % 150001, id % 10, 1
FROM filler;
ALTER TABLE item2tag ADD PRIMARY KEY (item_id, tag_id);
ALTER TABLE item2tag ADD KEY (tag_id);
-- Make tag 3 occur rarely.
UPDATE item2tag SET tag_id = 10 WHERE tag_id = 3 AND item_id > 0;
SELECT T1.item_id
FROM item2tag T1
JOIN item2tag T2 ON T1.item_id = T2.item_id
JOIN item2tag T3 ON T2.item_id = T3.item_id
WHERE T1.tag_id = 1 AND T2.tag_id = 2 AND T3.tag_id = 3;
SELECT item_id
FROM item2tag
WHERE tag_id=1 or tag_id=2 or tag_id=3
GROUP BY item_id
HAVING count(tag_id)=3;