如何在sql中高效地更新联接表(从我的ruby代码来看是高效的!!)

如何在sql中高效地更新联接表(从我的ruby代码来看是高效的!!),sql,postgresql,sql-execution-plan,Sql,Postgresql,Sql Execution Plan,这有点奇怪,我真的不明白,我在PostGreSql9.2中工作 我有这个数据库: movies (id, title, votes) infos (id, movie_id, info_type, value) 我想用infos.value更新movies.vows,加入movies.id=infos.movie\u当且仅当且仅当info\u type=100时,这是投票的类型 我尝试了两种不同的查询: update movies set votes = cast(i.value as int

这有点奇怪,我真的不明白,我在PostGreSql9.2中工作

我有这个数据库:

movies (id, title, votes)
infos (id, movie_id, info_type, value)
我想用infos.value更新movies.vows,加入movies.id=infos.movie\u当且仅当且仅当info\u type=100时,这是投票的类型

我尝试了两种不同的查询:

update movies
set votes = cast(i.value as integer)
from movies m inner join infos i on m.id = i.movie_id
where i.info_type = 100
使用explain预测的运行时间大约为1100万秒,这太多了

第二次尝试:

update movies m
set votes = cast(
(
  select value
  from infos i
  where i.info_type = 100 and i.movie_id = m.id
  limit 1
) AS integer);
这只需要2万秒。。还是太多了

我真的不知道查询计划是如何工作的,所以我尝试用一个使用active_record的ruby脚本来实现这一点。。。即:

Info.find_in_batches(:conditions => "info_type = 100") do |group|
    group.each{ |info|
        movie = Movie.find(info.movie_id)
        movie.votes = info.value.to_i
        movie.save
    }
end
对于那些不阅读ruby的人来说,这个查询只是循环遍历所有满足info_type=100条件的信息,然后对每一个信息搜索相应的电影并更新它

而且速度非常快!只需几分钟,就可以完成所有ruby/orm开销

现在,为什么??要知道,电影大约有60万张记录,但只有20万张三分之一有投票数的信息记录。但这并不能解释发生了什么

[…]使用explain预测的运行时间大约为1100万秒太多了

[…]这只需要2万秒。。还是太多了

我认为你误解了解释的结果。如中所述,估计的语句执行成本(即规划人员对运行语句所需时间的猜测)不是以秒为单位测量的,而是以成本单位测量的,这些成本单位是任意的,但通常意味着磁盘页获取

因此,PostgreSQL猜测第二条语句的运行速度将比第一条语句快500倍左右,但这两条语句的运行时间都没有您想象的那么长-

说明 正如@ruakh已经解释的,您可能误解了解释告诉您的内容。如果您想要以秒为单位的实际时间,请使用解释分析

但是要注意,这实际上执行了语句。我引述该手册:

重要提示:请记住,当分析 选项被使用。尽管解释将丢弃任何 SELECT将返回的输出,语句的其他副作用 一切照常进行。如果您希望在插入中使用解释分析, 更新、删除、创建表为或执行语句而不允许 如果命令影响数据,请使用以下方法:

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;
尽管如此,对第一次查询的估计仍然很高,表明问题严重

发生了什么? 至于第三种方法:对于大表,让数据库服务器一次更新整个大表总是比为每一行向服务器发送指令快一个数量级——如果新值来自数据库,则更是如此。更多。如果您的测试显示不是这样,那么您的测试设置可能有问题。事实上,它是

您的第一个查询完全出错。糟糕透顶的性能评估表明它是多么的错误。当您在FROM子句中将表电影连接到表信息时,您忘记了将结果行绑定到UPDATE表中的行的WHERE条件。这将导致交叉连接,即。E电影600k中的每一行都会使用值200k中的每一次投票进行更新,从而产生120000次更新。好吃的一切都错了。永远不要执行这个。即使在可以回滚的事务中也不行

你的第二个问题也出了问题。它运行一个相关子查询,即。E它为每一行运行一个单独的查询。这是600k子查询,而不是1个子查询,因此性能很差

对了,60万个子查询。不是20万。你指示Postgres更新每部电影,不管是什么。那些没有匹配infos.value no info_type=100的人,在投票中会收到一个空值,覆盖之前存在的任何内容

另外,我想知道限制1在那里做了什么

infos.movie\u id和infos.info\u类型都是唯一的,这样就不需要限制。 或者它不是独一无二的。然后,如果要保留结构,请向infos添加唯一索引。 正确的查询 这与您的第一个查询非常相似,只是简化并正确地执行了查询,再加上一个增强

不需要第二次加入电影。您只需要FROM子句中的信息

实际将要更新的行绑定到包含新值的行,从而避免意外的交叉连接:

避免空的更新,它们会带来成本而无收益。这就是最后一行的目的

应该是几秒钟或更短的时间,而不是几百万秒。
顺便说一句,索引对这个查询没有帮助,因为您使用了所有或三分之一的相关表,所以对于所描述的数据分布,表扫描速度更快。

谢谢您的回答,我知道限制1是无用的。。但现在我明白了为什么这么慢了,谢谢!我试过了,效果很好
UPDATE movies m
SET    votes = i.value::int
FROM   infos i
WHERE  m.id = i.movie_id
AND    i.info_type = 100
AND    m.votes IS DISTINCT FROM i.value::int;
WHERE  m.id = i.movie_id