MySQL INTERSECT通过连接表?

时间:2011-04-15 21:59:50

标签: mysql has-and-belongs-to-many intersect

所以基本上我有两个表,包含URL和TAGS,两者之间通过连接表TAGS_URLS具有has-and-belongs-to-many关系。

按标签查找网址的简单查询是:

SELECT urls.id FROM urls 
  INNER JOIN tags_urls ON urls.id=tags_urls.url_id
  INNER JOIN tags ON tags_urls.tag_id=tags.id 
WHERE tags.tag IN ("sample","tag","list");

但是,我正在尝试恢复包含所有标记集的所有URL的交集。即,只有包含标签“sample”和“tag”AND“list”的URL。

我有工作查询,但我无法在不到30秒的时间内执行查询。

SELECT a.id
  FROM
    (SELECT DISTINCT urls.id FROM urls
      INNER JOIN tags_urls ON tags_urls.url_id=urls.id INNER JOIN tags ON tags.id=tags_urls.tag_id
      WHERE tags.tag = 'sample') a
  JOIN
     (SELECT DISTINCT urls.id FROM urls
      INNER JOIN tags_urls ON tags_urls.url_id=urls.id INNER JOIN tags ON tags.id=tags_urls.tag_id
      WHERE tags.tag = 'list') b
  ON a.id = b.id;

结果集是正确的,但性能可怕。

我目前还在Redis数据库中将数据复制为存储在标记集中的URL ID列表,这样我就可以做到这样的事情并快速得到结果集。

SINTER "tag-sample" "tag-list"

通过合理的努力,是否有可能通过SINTER将此任务的MySQL性能提升到Redis级别?

2 个答案:

答案 0 :(得分:1)

我不是100%肯定,但我认为底层引擎正在为每个子选择创建一个临时表。根据数据的大小,这可能会非常昂贵。如果它们很大(并且它们在你的情况下),临时表必须将它们的内容写入磁盘,因为它们太大而不能立即保存在内存中。所以基本上你的查询是在复制大量数据,因为它试图构建两个匹配两个子选择的选择标准的临时表。一旦完成,它最终执行外部选择,这很可能相当快。

我会尝试将子选择的因子用于内连接。我认为以下内容将为您提供所需内容:

select urls.id from urls
inner join tags_urls tu1 on tu1.url_id = urls.id
inner join tags t1 on tu1.tag_id = t1.id and t1.tag = 'sample'
inner join tag_urls tu2 on tu2.url_id = urls.id
inner join tags t2 on t2.id = tu2.tag_id and t2.tag = 'list'

您将继续为要与之交叉的每个“标记”的tag_urls和标记添加成对的内部联接。再次,通过解释运行它,并确保一切都有正确的索引。

DBMS可以很好地处理几个内连接,但随着交叉点数量的增加,性能会降低。

答案 1 :(得分:0)

您可以尝试使用连接替换第二个statmenet中的sql子查询。 Robert Vieira在他的Sql Server书中声称,有时连接速度更快,有时子查询速度更快。很难相信MySql也不会这样。此外,如果表中有相当多的其他数据,除了'list'或'sample',那么您可能希望将此数据插入临时表并从该表运行查询。如果您要对该数据运行多个查询,则尤其如此。