SQL - 执行 UPDATE 的最快方法

Posted

技术标签:

【中文标题】SQL - 执行 UPDATE 的最快方法【英文标题】:SQL - Fastest way to do an UPDATE 【发布时间】:2014-09-12 10:17:04 【问题描述】:

我在 php/mysql 中创建了一个小程序来更新数据库中的表,我想知道在 UPDATE 语句中引入条件会如何改变它的性能。

现在我正在对表的每条记录执行 UPDATE,忽略了我要更新的字段已经计算和更新的事实:

mysql_query("UPDATE matches SET delta=".$delta." WHERE id=".$idp."");

其中$delta 是先前计算的PHP 变量,id 是表的唯一键。

插入条件以确定增量字段是否已在表中计算/更新,或者是否仍设置为 0(其默认值),在执行速度方面是否有任何差异?

mysql_query("UPDATE matches SET delta=".$delta." WHERE id=".$idp." and delta=0");

添加delta=0 条件有什么改进吗?

总结我的问题:由于delta=0 条件为假而未执行表上的写操作时,UPDATE 是否更快?我的表有大约 10k 条记录。

提前谢谢并原谅我糟糕的英语! ;-) 安德烈亚

【问题讨论】:

取决于您的数据和索引。 【参考方案1】:

如果您要进行这样的多次更新,那么通常我会建议您进行 INSERT,但在 id 已经存在时使用重复键条件进行更新。这样,您可能会执行一个 SQL 查询,而不是对表中的每条记录执行一个查询。

mysql_query("INSERT INTO matches (id, delta)
VALUES
($idp1, $delta1),
($idp2, $delta2),
($idp3, $delta3),
($idp4, $delta4)
ON DUPLICATE KEY UPDATE delta=VALUES(delta)
");

编辑

这是我在进行多次插入/更新时有时会使用的一个 php 类(这里是稍微简化的版本)

<?php

class table_process


    var $link;
    var $row_array = array();
    var $field_name_store = '';
    var $table_name = '';
    var $record_count = 0;
    var $update_clause = '';
    var $rows_to_process = 255;

    function __CONSTRUCT($link, $table_name, $update_clause = '')
    
        $this->link = $link;
        $this->table_name = $table_name;
        $this->update_clause = $update_clause;
    
    function __DESTRUCT()
    
        if (count($this->row_array) > 0)
        
            $this->process_rows();
        
    
    function field_names($in_field_names)
    
        $this->field_name_store = "(`".implode("`,`", $in_field_names)."`)";
    
    function record_count()
    
        return count($this->record_count);
    
    function make_row($in_row)
    
        $ret = true;
        foreach($in_row AS &$in_field)
        
            $in_field = (($in_field == null) ? "NULL" : "'".mysql_real_escape_string($in_field, $this->link)."'");
        
        $this->record_count++;
        $this->row_array[] = "(".implode(",", $in_row).")";
        if (count($this->row_array) > $this->rows_to_process)
        
            $ret =  $this->process_rows();
        
        return $ret;
    
    function process_rows()
    
        $ret = true;
        $sql = "INSERT INTO ".$this->table_name." ".$this->field_name_store." VALUES ".implode(",", $this->row_array)." ".$this->update_clause;
        if (!mysql_query($sql, $this->link))
        
            $ret = false;
        
        $this->row_array = array();
        return $ret;
    
    public function set_rows_to_process($rows_to_process)
    
        if (is_numeric($rows_to_process) and intval($rows_to_process) > 0)
        
            $this->rows_to_process = $rows_to_process;
        
    


?>

然后你可以像下面这样使用它:-

$ProcessMatches = new table_process($link, 'matches', ' ON DUPLICATE KEY UPDATE delta=VALUES(delta)', true);
$ProcessMatches->field_names(array('id', 'delta'));

$ProcessMatches ->make_row(array('id'=>$id1, 'delta'=>$delta1));
$ProcessMatches ->make_row(array('id'=>$id2, 'delta'=>$delta2));
$ProcessMatches ->make_row(array('id'=>$id3, 'delta'=>$delta3));
$ProcessMatches ->make_row(array('id'=>$id4, 'delta'=>$delta4));
$ProcessMatches ->make_row(array('id'=>$id5, 'delta'=>$delta5));
$ProcessMatches ->make_row(array('id'=>$id6, 'delta'=>$delta6));

unset($ProcessMatches);

该类采用连接链接和表名/列名,以及更新子句(如果需要)。

您插入的每一行都由该类获取并为插入而构建。一旦准备好插入 255 行,它就会插入它们并擦除存储它们的数组。完成后,取消设置触发 __DESTRUCT 方法的对象,该方法执行最终插入。

【讨论】:

我的 $idp 和 $delta 变量是 10.000+ 这不是问题吗? 可能,但是 10000 多个单独的更新语句将非常耗时。我倾向于一次将它们批处理到大约 250 个。我会用一些我有时使用的代码来更新答案。 谢谢@Kickstart;但仍然是我的最后一个问题:由于 delta=0 条件为假而未执行表上的写操作时,UPDATE 是否更快? 不确定更新。它将强制检查每条记录。这可能会加快速度,但我怀疑这将取决于有多少潜在的更新确实会导致更新。如果您将所有更新插入到临时表中,然后基于连接进行更新,但检查值是否已作为连接条件的一部分发生更改,则检查可能会改善情况。但这只是我对事情如何运作的感觉,我还没有做过任何检查。

以上是关于SQL - 执行 UPDATE 的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

清除 SQL 表的最快方法是啥?

增长 numpy 数值数组的最快方法

Oracle:使用 SQL 或 PL/SQL 提取文件扩展名的最快方法

oracle执行update语句卡住不动

mybatis执行批量更新batch update 的方法(oracle,mysql)

mybatis执行批量更新batch update 的方法(oracle,mysql)