Multiple Updates in MySQL

前端 未结 17 1000
南方客
南方客 2020-11-22 01:31

I know that you can insert multiple rows at once, is there a way to update multiple rows at once (as in, in one query) in MySQL?

Edit: For example I have the followi

相关标签:
17条回答
  • 2020-11-22 01:44

    use

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

    Please note:

    • id has to be a primary unique key
    • if you use foreign keys to reference the table, REPLACE deletes then inserts, so this might cause an error
    0 讨论(0)
  • 2020-11-22 01:45

    Use a temporary table

    // Reorder items
    function update_items_tempdb(&$items)
    {
        shuffle($items);
        $table_name = uniqid('tmp_test_');
        $sql = "CREATE TEMPORARY TABLE `$table_name` ("
            ."  `id` int(10) unsigned NOT NULL AUTO_INCREMENT"
            .", `position` int(10) unsigned NOT NULL"
            .", PRIMARY KEY (`id`)"
            .") ENGINE = MEMORY";
        query($sql);
        $i = 0;
        $sql = '';
        foreach ($items as &$item)
        {
            $item->position = $i++;
            $sql .= ($sql ? ', ' : '')."({$item->id}, {$item->position})";
        }
        if ($sql)
        {
            query("INSERT INTO `$table_name` (id, position) VALUES $sql");
            $sql = "UPDATE `test`, `$table_name` SET `test`.position = `$table_name`.position"
                ." WHERE `$table_name`.id = `test`.id";
            query($sql);
        }
        query("DROP TABLE `$table_name`");
    }
    
    0 讨论(0)
  • 2020-11-22 01:45

    Yes ..it is possible using INSERT ON DUPLICATE KEY UPDATE sql statement.. syntax: INSERT INTO table_name (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c)

    0 讨论(0)
  • 2020-11-22 01:49

    The question is old, yet I'd like to extend the topic with another answer.

    My point is, the easiest way to achieve it is just to wrap multiple queries with a transaction. The accepted answer INSERT ... ON DUPLICATE KEY UPDATE is a nice hack, but one should be aware of its drawbacks and limitations:

    • As being said, if you happen to launch the query with rows whose primary keys don't exist in the table, the query inserts new "half-baked" records. Probably it's not what you want
    • If you have a table with a not null field without default value and don't want to touch this field in the query, you'll get "Field 'fieldname' doesn't have a default value" MySQL warning even if you don't insert a single row at all. It will get you into trouble, if you decide to be strict and turn mysql warnings into runtime exceptions in your app.

    I made some performance tests for three of suggested variants, including the INSERT ... ON DUPLICATE KEY UPDATE variant, a variant with "case / when / then" clause and a naive approach with transaction. You may get the python code and results here. The overall conclusion is that the variant with case statement turns out to be twice as fast as two other variants, but it's quite hard to write correct and injection-safe code for it, so I personally stick to the simplest approach: using transactions.

    Edit: Findings of Dakusan prove that my performance estimations are not quite valid. Please see this answer for another, more elaborate research.

    0 讨论(0)
  • 2020-11-22 01:54
    UPDATE tableName SET col1='000' WHERE id='3' OR id='5'
    

    This should achieve what you'r looking for. Just add more id's. I have tested it.

    0 讨论(0)
  • 2020-11-22 01:55

    All of the following applies to InnoDB.

    I feel knowing the speeds of the 3 different methods is important.

    There are 3 methods:

    1. INSERT: INSERT with ON DUPLICATE KEY UPDATE
    2. TRANSACTION: Where you do an update for each record within a transaction
    3. CASE: In which you a case/when for each different record within an UPDATE

    I just tested this, and the INSERT method was 6.7x faster for me than the TRANSACTION method. I tried on a set of both 3,000 and 30,000 rows.

    The TRANSACTION method still has to run each individually query, which takes time, though it batches the results in memory, or something, while executing. The TRANSACTION method is also pretty expensive in both replication and query logs.

    Even worse, the CASE method was 41.1x slower than the INSERT method w/ 30,000 records (6.1x slower than TRANSACTION). And 75x slower in MyISAM. INSERT and CASE methods broke even at ~1,000 records. Even at 100 records, the CASE method is BARELY faster.

    So in general, I feel the INSERT method is both best and easiest to use. The queries are smaller and easier to read and only take up 1 query of action. This applies to both InnoDB and MyISAM.

    Bonus stuff:

    The solution for the INSERT non-default-field problem is to temporarily turn off the relevant SQL modes: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES",""). Make sure to save the sql_mode first if you plan on reverting it.

    As for other comments I've seen that say the auto_increment goes up using the INSERT method, this does seem to be the case in InnoDB, but not MyISAM.

    Code to run the tests is as follows. It also outputs .SQL files to remove php interpreter overhead

    <?
    //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).';');
    }
    
    0 讨论(0)
提交回复
热议问题