我有一个表sample
,其中有两列id
和cnt
,另一个表PostTags
有两列postid
和tagid
我想用相应的计数更新所有cnt值,我写了以下查询:
UPDATE sample SET
cnt = (SELECT COUNT(tagid)
FROM PostTags
WHERE sample.postid = PostTags.postid
GROUP BY PostTags.postid)
我打算立刻更新整个列,我似乎完成了这个。但在性能方面,这是最好的方法吗?或者有更好的方法吗?
修改
我已经运行了这个查询(没有GROUP BY)超过1小时的~18m记录。我正在寻找性能更好的查询。
答案 0 :(得分:1)
删除不必要的GROUP BY,语句看起来不错。但是,如果您希望许多sample.set已经包含正确的值,那么您将更新许多不需要更新的记录。这可能会产生一些开销(更大的回滚段,触发执行等),因此需要更长的时间。
为了仅更新需要更新的记录,请加入:
UPDATE sample
INNER JOIN
(
SELECT postid, COUNT(tagid) as cnt
FROM PostTags
GROUP BY postid
) tags ON tags.postid = sample.postid
SET sample.cnt = tags.cnt
WHERE sample.cnt != tags.cnt OR sample.cnt IS NULL;
这是SQL小提琴:http://sqlfiddle.com/#!2/d5e88。
答案 1 :(得分:1)
该查询不应该花费一个小时。我刚做了一个测试,在87520 keywords
的表上运行像你这样的查询,并在2776445 movie_keyword
行的多对多表中匹配行。在我的测试中,花了 32秒。
您可能缺少的关键部分是您必须在查阅列上有一个索引,在您的示例中为PostTags.postid
。
这是我的测试中的EXPLAIN(最后我们可以对MySQL 5.6中的UPDATE语句执行EXPLAIN):
mysql> explain update kc1 set count =
(select count(*) from movie_keyword
where kc1.keyword_id = movie_keyword.keyword_id) \G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: kc1
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 98867
Extra: Using temporary
*************************** 2. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: movie_keyword
type: ref
possible_keys: k_m
key: k_m
key_len: 4
ref: imdb.kc1.keyword_id
rows: 17
Extra: Using index
在keyword_id
上建立索引非常重要。就我而言,我有一个复合索引,但单列索引也有帮助。
CREATE TABLE `movie_keyword` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`movie_id` int(11) NOT NULL,
`keyword_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `k_m` (`keyword_id`,`movie_id`)
);
COUNT(*)
和COUNT(movie_id)
之间的区别应该是无关紧要的,假设movie_id
不可归零。但我使用COUNT(*)
因为如果我的索引仅在keyword_id
列上定义,它仍将被视为仅索引查询。