首先,对不起,如果标题没有意义,我就无法做到更好。我试图寻找类似的问题,但没有找到任何可以帮助我的问题。
简要说明:
我有一个产品数据库。产品有多个标签。我想用可用的标签过滤产品。我有一个已经有效的解决方案,但它表现出弱点,表中只有4万行。我想优化基础查询,所以从长远来看它不会伤害我。
包含代码 - 项目关系的表格
CREATE TABLE `tags_assoc` (
`assoc_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`tag_id` int(10) unsigned NOT NULL,
`item_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`assoc_id`),
KEY `tag_id` (`tag_id`),
KEY `item_id` (`item_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
页面的工作方式:
有很多标签。用户选择tag_id
和。{
页面已过滤,仅显示标有此标记的item_id
某些tag_id
。
后端检查已过滤的item_id
并列出所有内容
tag_id
可用于进一步过滤。
查询1 - 项目列表:
SELECT
item_id,
count(tag_id) as count
FROM
tags_assoc
WHERE
tag_id IN (...)
GROUP BY
item_id
HAVING
count = (...)
查询2 - 剩余标记列表:
SELECT
ta.tag_id,
t.tag_name,
COUNT(ta.tag_id) as count
FROM
tags_assoc ta
LEFT JOIN
tags t ON (ta.tag_id = t.tag_id)
WHERE
AND item_id IN (...)
AND ta.tag_id NOT IN (...)
GROUP BY
ta.tag_id
工作原理:
让我们在第一轮说我们选择tag_id = 250
。这意味着Query1将包含WHERE tag_id IN (250)
和HAVING count = 1
。
返回的列表将包含以下item_ids 1, 2, 3, 4, 5
。
现在,为了获得剩余的标签,我们使用以下参数运行Query2:AND item_id IN (1, 2, 3, 4, 5) AND ta.tag_id NOT IN (250)
。
用户从剩余的标签中选择其他标签,让我们说tag_id = 300
。这意味着Query1将包含WHERE tag_id IN (250, 300)
和HAVING count = 2
。
返回的列表将包含以下item_ids 1, 2, 3
。
要获取其余标记,我们使用以下参数运行Query2:AND item_id IN (1, 2, 3) AND ta.tag_id NOT IN (250, 300)
。
问题:
当用户选择第一个标记时,返回的item_id列表可能会很长。这意味着Query2也会很长,因为它可以包含以下内容:AND item_id IN (1, 2, 3, ... 7500, 7501, 7502)
。我有37k字符的长查询。关于性能,它并不可怕,但它可能会导致打嗝。这只是项目表中的7k行和40k行。
我已尝试的内容:
我重写了Query2不包含long item_id列表,而是使用类似于Query1中的子查询:AND item_id IN (SELECT item_id FROM tags_assoc WHERE tag_id IN (...) GROUP BY item_id HAVING COUNT(tag_id) = (...))
。表现明智,它太可怕了,所以我放弃了这个替代方案。
有人可以引导我走向更好的方向吗?
对不起,我希望我能尽可能清楚地解决这个问题。
答案 0 :(得分:0)
您可以将Q1中的输出保存在临时表中,然后使用它来提高Q2的效率。
我已经使用INNER JOIN来临时表来过滤Q2,尽管子查询也是一个选项。
CREATE TEMPORARY TABLE tempQ1
(
item_id int(10) unsigned NOT NULL,
PRIMARY KEY (item_id)
);
INSERT INTO tempQ1
SELECT
item_id,
count(tag_id) as count
FROM
tags_assoc
WHERE
tag_id IN (...)
GROUP BY
item_id
HAVING
count = (...);
-- Output tempQ1 as the first result
SELECT item_id FROM tempQ1;
SELECT
ta.tag_id,
t.tag_name,
FROM
tags_assoc ta
LEFT JOIN tags t ON (ta.tag_id = t.tag_id)
INNER JOIN tempQ1 ON ta.item_id = tempQ1.item_id
WHERE
ta.tag_id NOT IN (...)
GROUP BY
ta.tag_id;
DROP TEMPORARY TABLE tempQ1;