我不是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之外进行其余的得分匹配。
答案 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可以是任意顺序。)