73

How to update mysql data in bulk ? How to define something like this :

UPDATE `table` 
WHERE `column1` = somevalues
SET  `column2` = othervalues

with somevalues like :

VALUES
    ('160009'),
    ('160010'),
    ('160011');

and othervalues :

VALUES
    ('val1'),
    ('val2'),
    ('val3');

maybe it's impossible with mysql ? a php script ?

superhero
  • 6,281
  • 11
  • 59
  • 91
user3673384
  • 871
  • 2
  • 8
  • 13
  • Put your pairs in a lookup table and join through that to create your update. – LDMJoe Mar 01 '16 at 15:38
  • have you checked this answer http://stackoverflow.com/questions/12960569/mysql-bulk-insert-via-php – ELITE Mar 01 '16 at 16:08
  • it can be done by MySQL, without involving of PHP. Check out my answer below, it works like a charm. – Farside Mar 01 '16 at 16:09

5 Answers5

130

The easiest solution in your case is to use ON DUPLICATE KEY UPDATE construction. It works really fast, and does the job in easy way.

INSERT into `table` (id, fruit)
    VALUES (1, 'apple'), (2, 'orange'), (3, 'peach')
    ON DUPLICATE KEY UPDATE fruit = VALUES(fruit);

or to use CASE construction

UPDATE table
SET column2 = (CASE column1 WHEN 1 THEN 'val1'
                 WHEN 2 THEN 'val2'
                 WHEN 3 THEN 'val3'
         END)
WHERE column1 IN(1, 2 ,3);
Farside
  • 9,923
  • 4
  • 47
  • 60
  • Is this safe to use even when the column `fruit` is not unique? I'm confused by the statement in the mysql docs: "If you specify an ON DUPLICATE KEY UPDATE clause and a row to be inserted would cause a duplicate value in a UNIQUE index or PRIMARY KEY, an UPDATE of the old row occurs" in https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html. – kovac Feb 20 '18 at 04:29
  • @swdon, the question you asked contradicts to the basic knowledge of MySQL. PRIMARY KEYs are UNIQUE. Table's [primary key](https://dev.mysql.com/doc/refman/5.5/en/glossary.html#glos_primary_key) by definition is a unique key. – Farside Feb 20 '18 at 14:03
  • 1
    Sorry, I actually mixed up. In my case primary key is an auto incremented value and the update statement was checking for some other column that is neither primary key nor unique. But I got what you meant in the original answer. – kovac Feb 20 '18 at 23:18
  • 2
    This "ON DUPLICATE KEY" looks like a good technique, but note (no mysql) if any of the field in the table has no default value, it will fail to execute. So you have to push some placeholder value to pass this restriction, which decrease the portability of this solution. – Dankó Dávid Oct 19 '18 at 08:36
  • 3
    Be careful with "ON DUPLICATE KEY" statements in MySQL, they will cause your auto_increment column to increase even if you're not inserting a new row. You can burn through A LOT of auto_increment values very quickly, if you are bulk updating with "ON DUPLICATE KEY". Make sure your auto_increment field is big enough to allow for this. – Jeff Kilbride Jul 09 '21 at 15:54
  • This seems very slow in large table. But in my experience, updates by transaction was fast. e.g. start transaction;update1;update 2;... commit; – Liki Crus Dec 02 '21 at 21:26
  • @JeffKilbride I don't understand why you said "they will cause your auto_increment column to increase even if you're not inserting a new row". I made a test, inserted a record after I did a batch update by using "ON DUPLICATE KEY" statements, and found the the id is right after the last id(eg: if last id is 1000, the new record's id is 1001), I didn't found auto_increment column increased. – 无名小路 Dec 31 '21 at 08:55
  • MySQL first increments the auto_increment field, then checks for duplicate values. If you use "ON DUPLICATE KEY", it can lead to gaps in your auto_increment field. See this answer for more info: https://stackoverflow.com/a/23517191/5605726. But, honestly, I don't know if this still applies to MySQL 8+. It's just something I'm always careful about. – Jeff Kilbride Jan 01 '22 at 19:45
  • I have seen big problems with deadlocks as well using this method. (on mysql 5.6 and 5.7) so I would probably avoid it if you have high concurrency – Gabriel Cappelli Feb 24 '23 at 09:24
13

If the "bulk" data you have is dynamic and is coming from PHP (you did tag it, after all), then the query would look something like this:

INSERT INTO `foo` (id, bar)
VALUES 
    (1, 'pineapple'),
    (2, 'asian pear'),
    (5, 'peach')
ON DUPLICATE KEY UPDATE bar = VALUES(bar);

and the PHP to generate this from an existing array (assuming the array is of a format like:

$array = (
    somevalues_key => othervalues_value
);

) would look something like this (by no means the best (doesn't address escaping or sanitizing the values, for instance), just an quick example):

$pairs = array();
foreach ($array as $key => $value) {
    $pairs[] = "($key, '$value')";
}

$query = "INSERT INTO `foo` (id, bar) VALUES " . implode(', ', $pairs) . " ON DUPLICATE KEY UPDATE bar = VALUES(bar)";
Davis
  • 856
  • 4
  • 11
  • Davis, When i run the top statement you wrote it works in a table where there is only id and bar columns. But if the table contains more than id and bar, such as id,bar,date it doesnt work. Do you have to specify each column even if it doesnt need updating ? – Bonxy May 26 '18 at 10:31
  • @Bonxy, in that case your query should modify as following, it did work for me. INSERT INTO `foo` (id, bar, date) VALUES (1, 'pineapple', d1), (2, 'asian pear', d2), (5, 'peach', d3) ON DUPLICATE KEY UPDATE bar = VALUES(bar), date=values(date); – Satys May 24 '19 at 15:09
1

You could try an UPDATE with JOIN as below:

UPDATE table
INNER JOIN (
            SELECT 1 column1, 2 column2, 10 new_v1, 20 new_v2, 30 new_v3
  UNION ALL SELECT 4 column1, 5 column2, 40 new_v1, 50 new_v2, 60 new_v3
) updates
  ON table.column1 = updates.column1
  AND table.column2 = updates.column2
SET
  table.column1 = updates.new_v1,
  table.column2 = updates.new_v2,
  table.column3 = updates.new_v3;

As long as you can craft the inner SELECT statements from the updates subquery you would get the benefit of running all these updates in a single statement (which should give you some performance boost on InnoDB depending on your table size).

Razvan
  • 2,436
  • 18
  • 23
  • Although this looked to be a cleaner SQL solution than the `CASE WHEN THEN` answer, I ran into charset/collation complexities as the temporary `INNER JOIN (SELECT...)` table won’t automatically be the same as the parent table leading to “incorrect string value” errors with utf8mb4 values. – chris Aug 08 '22 at 09:55
0

If you are using a drag & drop tableView or collectionView to sort datas in your app, like allowing users to arrange their photos by drag and drop functionality, send a comma seperated list of ordered ids to the backend after user edits finish.

In your backend, explode ids to the an array like

$new_ranks = array();
$supplied_orders = explode(",", $_POST["supplied_new_order"]); //52,11,6,54,2 etc
$start_order = 99999;
foreach ($supplied_orders as $supplied_row_id) {
    //your all validations... make sure supplied_row_id belongs to that user or not etc..

    $new_ranks[intval($supplied_row_id)] = $start_order--;
}

now, you can update all new ranks like @Farside recommendation 2.

if (count($new_ranks) > 0) {
    $case_sqls = array(); 
    foreach ($new_ranks as $id => $rank) {
        $case_sqls[] = "WHEN ".intval($id)." THEN ".intval($rank)."";
    } 
    $case_sql = implode(" ", $case_sqls);

    $this->db->query("
        UPDATE 
            service_user_medias
        SET 
            rank = (CASE id ".$case_sql." END)
        WHERE 
            id IN(".implode(",", array_keys($new_ranks)).");
    ");
}
woheras
  • 71
  • 1
  • 4
-7

If you have data in array format then try this

and your query is like "UPDATE table WHERE column1 = ? SET column2 = ?"

then set it like below

foreach($data as $key => $value) {
    $query->bind_param('ss', $key, $value);
    $query->execute();
}

hope it'll work.

Reference from this.

Community
  • 1
  • 1
Virus
  • 70
  • 4