在聚合查询

时间:2015-08-18 01:21:19

标签: mysql performance indexing subquery aggregate-functions

我不是SQL专家,但我相信我已经解决了我的问题,尽管是以一种相当不高效的方式。我希望有人可以指出比我想出的更好的方法。我试图在由RelevanSSI(Wordpress的全文搜索插件)创建的术语索引中找到重复或类似的内容 - 但这是在Wordpress安装之外发生的,它是实际的数据库,所以Wordpress,它的API和任何其他表通常与之相关的内容超出了此范围。

RelevanSSI索引表如下所示:

CREATE TABLE `wp_relevanssi` (
 `doc` bigint(20) NOT NULL DEFAULT '0',
 `term` varchar(50) NOT NULL DEFAULT '0',
 `content` mediumint(9) NOT NULL DEFAULT '0',
 `title` mediumint(9) NOT NULL DEFAULT '0',
 `comment` mediumint(9) NOT NULL DEFAULT '0',
 `tag` mediumint(9) NOT NULL DEFAULT '0',
 `link` mediumint(9) NOT NULL DEFAULT '0',
 `author` mediumint(9) NOT NULL DEFAULT '0',
 `category` mediumint(9) NOT NULL DEFAULT '0',
 `excerpt` mediumint(9) NOT NULL DEFAULT '0',
 `taxonomy` mediumint(9) NOT NULL DEFAULT '0',
 `customfield` mediumint(9) NOT NULL DEFAULT '0',
 `mysqlcolumn` mediumint(9) NOT NULL DEFAULT '0',
 `taxonomy_detail` longtext NOT NULL,
 `customfield_detail` longtext NOT NULL,
 `mysqlcolumn_detail` longtext NOT NULL,
 `type` varchar(210) NOT NULL DEFAULT 'post',
 `item` bigint(20) NOT NULL DEFAULT '0',
 `term_reverse` varchar(50) NOT NULL DEFAULT '0',
 UNIQUE KEY `doctermitem` (`doc`,`term`,`item`),
 KEY `terms` (`term`(20)),
 KEY `docs` (`doc`),
 KEY `typeitem` (`type`,`item`),
 KEY `relevanssi_term_reverse_idx` (`term_reverse`(10))
) ENGINE=InnoDB DEFAULT CHARSET=utf8

我通过以下查询成功获得了(我认为)我想要的信息:

SELECT r1.doc, r2.doc, 
    50 * COUNT( r1.term ) * (
        (c1.total + c2.total) / 
        ( c1.total * c2.total ) 
    ) AS ScorePct
FROM  `wp_relevanssi` r1
LEFT JOIN  `wp_relevanssi` r2 
ON r1.term = r2.term
AND r1.doc > r2.doc
AND r1.type = r2.type
AND (r1.content > 0 or r1.title > 0 or r1.taxonomy > 0 or r1.tag > 0)
AND (r2.content > 0 or r2.title > 0 or r2.taxonomy > 0 or r2.tag > 0)
LEFT JOIN (
    SELECT doc, COUNT( term ) AS total
    FROM  `wp_relevanssi` 
    GROUP BY doc
) c1 
ON r1.doc = c1.doc
LEFT JOIN (
    SELECT doc, COUNT( term ) AS total
    FROM  `wp_relevanssi` 
    GROUP BY doc
) c2 
ON r2.doc = c2.doc
GROUP BY r1.doc, r2.doc
HAVING ScorePct >50
ORDER BY ScorePct DESC

我的问题是掉入连接的那些大笨拙的子查询。我认为我至少需要一个子查询来执行此操作(实际上,获取特定文档的术语总数),因为在第一个LEFT JOIN之后我们只有关于主查询中匹配术语的信息,丢弃了不匹配的。 (请继续告诉我,我错在这里,我很想知道子查询是不必要的)。

除此之外,我是否有办法使用单个子查询执行此操作,或以其他方式提高此查询的性能?我完全希望它是一个非常繁重的查询,我对此没有任何疑虑,但我希望尽可能好地运行它。

编辑:所以我只需要用不同的方法解决这个问题 - 通过一次查看单个文档(当文档被更改)我可以简化查询:

SELECT r1.doc, r2.doc, count(*) AS matches
FROM  `wp_relevanssi` r1
INNER JOIN  `wp_relevanssi` r2 
ON r1.term = r2.term
AND r1.doc <> r2.doc
AND r1.type = r2.type
AND (r1.content > 0 or r1.title > 0 or r1.taxonomy > 0 or r1.tag > 0)
AND (r2.content > 0 or r2.title > 0 or r2.taxonomy > 0 or r2.tag > 0)
WHERE r1.doc = %d
GROUP BY r1.doc, r2.doc
ORDER BY matches DESC
LIMIT 0,10

即使有650,000行,它也会在合理的时间内运行,并跟进:

SELECT doc, COUNT( term ) AS total
FROM  `wp_relevanssi` 
WHERE doc IN (%d,%d,%d...)
GROUP BY doc

然后在DB之外进行其余的得分匹配。

1 个答案:

答案 0 :(得分:1)

  • COUNT(term)表示您需要对term进行NOT NULL测试。如果没有,那么只需说出COUNT(*)

  • 您的LEFT JOINs似乎相同;是什么赋予了?见下文。

  • JOIN ( SELECT ... )

  • LEFT表示&#39;表&#39;在&#39;权利&#39;可能缺少行,但在这种情况下您需要NULLs。你需要吗?

  • &#34;前缀&#34;索引(KEY terms (term(20)))很少有用,并且通常会阻止使用索引。删除(20)

  • InnoDB表应该有一个明确的PRIMARY KEY。您拥有的UNIQUE密钥可以转换为它。

  • 此查询似乎是O(N * N)。也就是说,当你增加wp_relevanssi中的行数(N)时,它将迅速(即,平方)变慢。

对于dup子查询,请考虑以下内容并在两个位置使用term_counts

CREATE TABLE term_counts (
    PRIMARY KEY(doc)
)
    SELECT doc,
           COUNT( term ) AS total
        FROM  `wp_relevanssi` 
        GROUP BY doc;

因为这个

(r1.content > 0 or r1.title > 0 or r1.taxonomy > 0 or r1.tag > 0)

您应该考虑将未通过该过滤的所有行复制到另一个表中,然后使用该表。

因为

ON r1.term = r2.term
AND r1.doc > r2.doc
AND r1.type = r2.type

我同意

INDEX(term, type, doc)

(doc必须是last,term和type可以是任意顺序。)