在mysql中有更好的方法吗? - 使用另一个select和group by更新整个列

时间:2014-04-16 14:16:02

标签: mysql sql performance group-by sql-update

我有一个表sample,其中有两列idcnt,另一个表PostTags有两列postidtagid

我想用相应的计数更新所有cnt值,我写了以下查询:

UPDATE sample SET
cnt = (SELECT COUNT(tagid) 
       FROM PostTags 
       WHERE sample.postid = PostTags.postid 
       GROUP BY PostTags.postid)

我打算立刻更新整个列,我似乎完成了这个。但在性能方面,这是最好的方法吗?或者有更好的方法吗?

修改

我已经运行了这个查询(没有GROUP BY)超过1小时的~18m记录。我正在寻找性能更好的查询。

2 个答案:

答案 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列上定义,它仍将被视为仅索引查询。