我知道你可以一次插入多行,是否有一种方法可以一次更新多行(如在,在一个查询)在MySQL?

编辑: 例如,我有以下内容

Name   id  Col1  Col2
Row1   1    6     1
Row2   2    2     3
Row3   3    9     5
Row4   4    16    8

我想将以下所有更新组合成一个查询

UPDATE table SET Col1 = 1 WHERE id = 1;
UPDATE table SET Col1 = 2 WHERE id = 2;
UPDATE table SET Col2 = 3 WHERE id = 3;
UPDATE table SET Col1 = 10 WHERE id = 4;
UPDATE table SET Col2 = 12 WHERE id = 4;

当前回答

您可能还对在更新上使用连接感兴趣,这也是可能的。

Update someTable Set someValue = 4 From someTable s Inner Join anotherTable a on s.id = a.id Where a.id = 4
-- Only updates someValue in someTable who has a foreign key on anotherTable with a value of 4.

编辑:如果要更新的值不是来自数据库中的其他地方,则需要发出多个更新查询。

其他回答

这些都适用于InnoDB。

我觉得了解这3种不同方法的速度很重要。

有3种方法:

插入:插入时没有重复的密钥更新 TRANSACTION:对事务中的每个记录进行更新 CASE:在这种情况下,您可以在UPDATE中为每个不同的记录设置CASE /时间

我刚刚测试了一下,对我来说,INSERT方法比TRANSACTION方法快6.7倍。我同时尝试了3000行和30000行。

TRANSACTION方法仍然必须单独运行每个查询,这需要时间,尽管它在执行时将结果批处理在内存或其他地方。TRANSACTION方法在复制和查询日志中也非常昂贵。

更糟糕的是,CASE方法比有30000条记录的INSERT方法慢41.1倍(比TRANSACTION慢6.1倍)。MyISAM慢75倍。INSERT和CASE方法在大约1000条记录中保持平衡。即使是在100条记录中,CASE方法也仅仅快一点。

所以一般来说,我觉得INSERT方法是最好的,也是最容易使用的。查询更小,更容易阅读,并且只占用1个查询操作。InnoDB和MyISAM都适用。

附加材料:

解决INSERT非默认字段问题的方法是临时关闭相关的SQL模式:SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES","")。如果计划恢复sql_mode,请确保首先保存它。

至于我看到的其他评论说auto_increment是使用INSERT方法增加的,这似乎在InnoDB中是这样的,但在MyISAM中不是。

运行测试的代码如下所示。它还输出.SQL文件以消除php解释器开销

<?php
//Variables
$NumRows=30000;

//These 2 functions need to be filled in
function InitSQL()
{

}
function RunSQLQuery($Q)
{

}

//Run the 3 tests
InitSQL();
for($i=0;$i<3;$i++)
    RunTest($i, $NumRows);

function RunTest($TestNum, $NumRows)
{
    $TheQueries=Array();
    $DoQuery=function($Query) use (&$TheQueries)
    {
        RunSQLQuery($Query);
        $TheQueries[]=$Query;
    };

    $TableName='Test';
    $DoQuery('DROP TABLE IF EXISTS '.$TableName);
    $DoQuery('CREATE TABLE '.$TableName.' (i1 int NOT NULL AUTO_INCREMENT, i2 int NOT NULL, primary key (i1)) ENGINE=InnoDB');
    $DoQuery('INSERT INTO '.$TableName.' (i2) VALUES ('.implode('), (', range(2, $NumRows+1)).')');

    if($TestNum==0)
    {
        $TestName='Transaction';
        $Start=microtime(true);
        $DoQuery('START TRANSACTION');
        for($i=1;$i<=$NumRows;$i++)
            $DoQuery('UPDATE '.$TableName.' SET i2='.(($i+5)*1000).' WHERE i1='.$i);
        $DoQuery('COMMIT');
    }
    
    if($TestNum==1)
    {
        $TestName='Insert';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf("(%d,%d)", $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery('INSERT INTO '.$TableName.' VALUES '.implode(', ', $Query).' ON DUPLICATE KEY UPDATE i2=VALUES(i2)');
    }
    
    if($TestNum==2)
    {
        $TestName='Case';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf('WHEN %d THEN %d', $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery("UPDATE $TableName SET i2=CASE i1\n".implode("\n", $Query)."\nEND\nWHERE i1 IN (".implode(',', range(1, $NumRows)).')');
    }
    
    print "$TestName: ".(microtime(true)-$Start)."<br>\n";

    file_put_contents("./$TestName.sql", implode(";\n", $TheQueries).';');
}

为什么没有人在一个查询中提到多个语句?

在php中,使用mysqli实例的multi_query方法。

来自php手册

MySQL允许在一个语句字符串中包含多条语句。一次发送多个语句可以减少客户端-服务器之间的往返,但需要特殊处理。

下面是更新30,000 raw中与其他3种方法的比较结果。代码可以在这里找到,这是基于@Dakusan的回答

事务:5.5194580554962 插入:0.20669293403625 例:16.474853992462 多:0.0412278175354

如您所见,多语句查询比最高答案更有效。

如果你得到这样的错误信息:

PHP Warning:  Error while sending SET_OPTION packet

你可能需要增加mysql配置文件中的max_allowed_packet,在我的机器是/etc/mysql/my.cnf,然后重新启动mysqld。

use

REPLACE INTO`table` VALUES (`id`,`col1`,`col2`) VALUES
(1,6,1),(2,2,3),(3,9,5),(4,16,8);

请注意:

Id必须是一个主唯一键 如果使用外键来 引用表,REPLACE删除然后插入,所以这可能 造成错误

下面是简单的方法

update my_table m, -- let create a temp table with populated values
    (select 1 as id, 20 as value union -- this part will be generated
     select 2 as id, 30 as value union -- using a backend code
     -- for loop 
     select N as id, X as value
        ) t
set m.value = t.value where t.id=m.id -- now update by join - quick
UPDATE `your_table` SET 

`something` = IF(`id`="1","new_value1",`something`), `smth2` = IF(`id`="1", "nv1",`smth2`),
`something` = IF(`id`="2","new_value2",`something`), `smth2` = IF(`id`="2", "nv2",`smth2`),
`something` = IF(`id`="4","new_value3",`something`), `smth2` = IF(`id`="4", "nv3",`smth2`),
`something` = IF(`id`="6","new_value4",`something`), `smth2` = IF(`id`="6", "nv4",`smth2`),
`something` = IF(`id`="3","new_value5",`something`), `smth2` = IF(`id`="3", "nv5",`smth2`),
`something` = IF(`id`="5","new_value6",`something`), `smth2` = IF(`id`="5", "nv6",`smth2`) 

//你只是在php中构建它

$q = 'UPDATE `your_table` SET ';

foreach($data as $dat){

  $q .= '

       `something` = IF(`id`="'.$dat->id.'","'.$dat->value.'",`something`), 
       `smth2` = IF(`id`="'.$dat->id.'", "'.$dat->value2.'",`smth2`),';

}

$q = substr($q,0,-1);

所以你可以用一个查询更新孔表