MySQL多标签选择

时间:2014-10-15 12:51:17

标签: mysql

首先,对不起,如果标题没有意义,我就无法做到更好。我试图寻找类似的问题,但没有找到任何可以帮助我的问题。

简要说明:
我有一个产品数据库。产品有多个标签。我想用可用的标签过滤产品。我有一个已经有效的解决方案,但它表现出弱点,表中只有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;

页面的工作方式:

  1. 有很多标签。用户选择tag_id和。{ 页面已过滤,仅显示标有此标记的item_id 某些tag_id

  2. 后端检查已过滤的item_id并列出所有内容 tag_id可用于进一步过滤。

  3. 查询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) = (...))。表现明智,它太可怕了,所以我放弃了这个替代方案。

    有人可以引导我走向更好的方向吗?
    对不起,我希望我能尽可能清楚地解决这个问题。

1 个答案:

答案 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;