218

I am trying to understand how to UPDATE multiple rows with different values and I just don't get it. The solution is everywhere but to me it looks difficult to understand.

For instance, three updates into 1 query:

UPDATE table_users
SET cod_user = '622057'
    , date = '12082014'
WHERE user_rol = 'student'
    AND cod_office = '17389551'; 

UPDATE table_users
SET cod_user = '2913659'
    , date = '12082014'
WHERE user_rol = 'assistant'
    AND cod_office = '17389551'; 

UPDATE table_users
SET cod_user = '6160230'
    , date = '12082014'
WHERE user_rol = 'admin'
    AND cod_office = '17389551'; 

I read an example, but I really don't understand how to make the query. i.e:

UPDATE table_to_update
SET cod_user= IF(cod_office = '17389551','622057','2913659','6160230')
    ,date = IF(cod_office = '17389551','12082014')
WHERE ?? IN (??) ;

I'm not entirely clear how to do the query if there are multiple condition in the WHERE and in the IF condition..any ideas?

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
franvergara66
  • 10,524
  • 20
  • 59
  • 101
  • 1
    Does this answer your question? [Multiple Updates in MySQL](https://stackoverflow.com/questions/3432/multiple-updates-in-mysql) – Elie Faës Jan 22 '20 at 10:45

8 Answers8

297

You can do it this way:

UPDATE table_users
    SET cod_user = (case when user_role = 'student' then '622057'
                         when user_role = 'assistant' then '2913659'
                         when user_role = 'admin' then '6160230'
                    end),
        date = '12082014'
    WHERE user_role in ('student', 'assistant', 'admin') AND
          cod_office = '17389551';

I don't understand your date format. Dates should be stored in the database using native date and time types.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • 1
    how I can do so that the update is executed, if the record already exists – franvergara66 Sep 07 '14 at 02:21
  • 2
    @franvergara66 . . . I don't understand your comment. `update`s only affect records that already exist. – Gordon Linoff Sep 07 '14 at 02:47
  • Excuse my english sir, when i try to make an update mysql give me the error: # 1062 - Duplicate entry 'XXX' for key 'PRIMARY'. That does when I try to update a record with the same value it already had, is there any way to skip the update if the current value is the same as being updated? – franvergara66 Sep 07 '14 at 03:03
  • 1
    @franvergara66 . . . You may have a different problem. If `cod_user` is a primary key and the values are being shuffled around, then multiple updates is probably the best route. – Gordon Linoff Sep 07 '14 at 03:06
  • 1
    yeah, i see, the real problem is that there are several assistants, then when trying to do an update, the integrity of the primary key is violated. Thank you very much for your time. – franvergara66 Sep 07 '14 at 11:58
  • I had an error with your code. I fixed the problem by changing the WHERE clause for `WHERE user_rol in ('student', 'assistant', 'admin')` like Hart CO suggested. – pmrotule Apr 13 '15 at 22:10
  • if one of them fails due to unique constrain or something, will the other `INSERT`s fail as well? – Fanckush Jun 16 '16 at 13:35
  • 2
    Be aware, this will set user_role to `NULL` for any record that is returned that doesn't match one of the three cases. Consider adding an ELSE before the end like this `ELSE cod_user` so that you set the field to itself if it doesn't match the WHEN clause. – Andrew Sep 23 '16 at 04:22
  • @GordonLinoff could you pls help me to simplify my commands, https://justpaste.it/1iv1b – Gem Mar 28 '18 at 05:48
  • the last part of the WHERE clause does not match the original query: `AND cod_office = '17389551'` should be `AND cod_office = '123456'` – Adriaan Koster Oct 19 '18 at 12:33
  • @AdriaanKoster . . . It is unclear why, but the question was changed (by someone else) years after it was asked and answered. You can look at the edit history of the question. – Gordon Linoff Oct 19 '18 at 12:46
  • @GordonLinoff why must we have the where clause, isn't the case making enough conditions? Thanks – Valter Ekholm Feb 19 '20 at 14:04
  • @ValterEkholm . . . The OP wants to update a particular set of rows defined by the `WHERE` clause. Those columns might have other values. – Gordon Linoff Feb 19 '20 at 15:33
  • Thanks @GordonLinoff - I jumped over the fact that I used Gordons code with my project - another table structure (my code was with nodes in a tree where I with this help could swap two nodes positions in one query) - and I didn't look much at the actual question. Anyway, in my code i had to specify the nodes (id's) to be swapped in the where clause event though the case-clause mentioned those id's already. I tried first without the where-clause and fields where nulled… or so. – Valter Ekholm Feb 21 '20 at 08:16
  • Just referenced this method in https://dba.stackexchange.com/questions/287028/mysql-on-duplicate-key-update-monitoring thanks for documenting. It seems slower than just doing an insert with ON DUPLICATE. I'd really appreciate any advice on that aspect with performance on that post. – MrMesees Mar 19 '21 at 09:50
  • Performance-wise, is this preferable to just executing an UPDATE per record? – Hatchmaster J Nov 23 '22 at 07:29
178

MySQL allows a more readable way to combine multiple updates into a single query. This seems to better fit the scenario you describe, is much easier to read, and avoids those difficult-to-untangle multiple conditions.

INSERT INTO table_users (cod_user, date, user_rol, cod_office)
VALUES
('622057', '12082014', 'student', '17389551'),
('2913659', '12082014', 'assistant','17389551'),
('6160230', '12082014', 'admin', '17389551')
ON DUPLICATE KEY UPDATE
 cod_user=VALUES(cod_user), date=VALUES(date)

This assumes that the user_rol, cod_office combination is a primary key. If only one of these is the primary key, then add the other field to the UPDATE list. If neither of them is a primary key (that seems unlikely) then this approach will always create new records - probably not what is wanted.

However, this approach makes prepared statements easier to build and more concise.

John
  • 1
  • 13
  • 98
  • 177
Trevedhek
  • 4,138
  • 2
  • 20
  • 16
  • 7
    Thank you! This is what I was searching for a long time, the cleanest looking way, I couldn't figure out this syntax, specifically `cod_user=VALUES(cod_user), ...`, even from official MySQL 5.6 docs – Yuriy Dyachkov Jul 06 '16 at 12:36
  • 42
    Note: This will add new rows if the key does not exist in the table resulting in unwanted records. – Faraz Jul 17 '16 at 07:53
  • 3
    Tricky to use a never inserting IODKU, yet very elegant. – Tom Desp Jun 06 '17 at 18:23
  • 3
    And note that this approach **require** primary key is set for the table. – FlameStorm Sep 06 '17 at 22:35
  • It works.This kind of sql is faster then a lot of sqls. The limit is 65535 placeholder. – bronze man Sep 27 '17 at 08:32
  • 16
    This doesn't work if you omit any columns that can't be null, since sql still tries to create new record before actually resorting to update. – Arno van Oordt Nov 21 '17 at 15:18
  • You could probably put a FROM and WHERE clause on this to make sure you don't actually insert anything, right? – Joe Phillips Apr 16 '18 at 21:58
  • @bronzeman Note that the 65535 placeholder limit does not apply if PDO emulates prepared statements: `$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true)`. – BenMorel Jun 21 '18 at 22:17
  • Works like a charm. Can be used for more dynamic cases than the one described above. Thank you – White Bullet May 04 '19 at 07:25
  • re: Joe Phillips - I'm wondering the exact same thing as I like this structure for updates. I don't think that's feasible as the where clause would be shared between the insert and update clauses (a clause that prevents insert may prevent update) – Ben A. Hilleli Aug 28 '19 at 06:12
  • Thank you! This gave me a sorely-needed 3X speedup. I think your answer is better than the accepted answer. – UserX Sep 26 '20 at 05:52
  • This answer is better than the accepted one because it allows to insert lacking rows at the same time, and it is much more handy to generate the query itself. – Zorann Oct 26 '20 at 14:02
  • 1
    @ArnovanOordt Any ideas on how to ignore null columns? I'm running into this locally now. However, I have this same exact thing working for a Prod version of the App and on a cloud JawsdDB instance. That's strange, not sure how it's not breaking there. The sql server engines are very similar – user2402616 Jan 27 '21 at 20:26
  • 1
    @user2402616 Not sure what I used it for back then so I don't know how I fixed it :( Probably with some separate sql calls. – Arno van Oordt Jan 28 '21 at 07:54
  • Yeah, it's been a while. I removed the `NOT NULL` constraints and it works locally for me now. Any idea how this is not failing on my Prod server? Maybe it sees it as a warning and bypasses? – user2402616 Jan 28 '21 at 14:19
  • 1
    It is not necessary for `user_rol, cod_office` pair to be the primary key, they can belong to the UNIQUE index. – cl0ne May 13 '21 at 10:40
  • This is the way to go. I would however replace the `INSERT` with a `REPLACE` and remove the `ON DUPLICATE` part. – Angelin Calu Mar 11 '22 at 08:49
  • @Angelin Calu. Replace will mess up any auto_increment keys and triggers on delete and on insert on foreign keys. – grim_i_am Jul 12 '22 at 19:46
20
UPDATE table_name
SET cod_user = 
    CASE 
    WHEN user_rol = 'student' THEN '622057'
    WHEN user_rol = 'assistant' THEN '2913659'
    WHEN user_rol = 'admin' THEN '6160230'
    END, date = '12082014'

WHERE user_rol IN ('student','assistant','admin')
AND cod_office = '17389551';
Saghachi
  • 851
  • 11
  • 19
Akshay Bhan
  • 245
  • 4
  • 12
16

You can use a CASE statement to handle multiple if/then scenarios:

UPDATE table_to_update 
SET 
    cod_user = CASE
        WHEN user_rol = 'student'   THEN '622057'
        WHEN user_rol = 'assistant' THEN '2913659'
        WHEN user_rol = 'admin'     THEN '6160230'
    END,
    date = '12082014'
WHERE user_rol IN ('student','assistant','admin')
  AND cod_office = '17389551';
Tomasz Kowalczyk
  • 10,472
  • 6
  • 52
  • 68
Hart CO
  • 34,064
  • 6
  • 48
  • 63
5

To Extend on @Trevedhek answer,

In case the update has to be done with non-unique keys, 4 queries will be need

NOTE: This is not transaction-safe

This can be done using a temp table.

Step 1: Create a temp table keys and the columns you want to update

CREATE TEMPORARY TABLE  temp_table_users
(
    cod_user varchar(50)
    , date varchar(50)
    , user_rol varchar(50)
    ,  cod_office varchar(50)
) ENGINE=MEMORY

Step 2: Insert the values into the temp table

Step 3: Update the original table

UPDATE table_users t1
JOIN temp_table_users tt1 using(user_rol,cod_office)
SET 
t1.cod_office = tt1.cod_office
t1.date = tt1.date

Step 4: Drop the temp table

Sab
  • 485
  • 5
  • 17
2

In php, you use multi_query method of mysqli instance.

$sql = "SELECT COUNT(*) AS _num FROM test;
        INSERT INTO test(id) VALUES (1); 
        SELECT COUNT(*) AS _num FROM test; ";

$mysqli->multi_query($sql);

comparing result to transaction, insert, case methods in update 30,000 raw.

Transaction: 5.5194580554962
Insert: 0.20669293403625
Case: 16.474853992462
Multi: 0.0412278175354

As you can see, multiple statements query is more efficient than the highest answer.

Just in case if you get error message like this:

PHP Warning:  Error while sending SET_OPTION packet

You may need to increase the max_allowed_packet in mysql config file.

Saghachi
  • 851
  • 11
  • 19
  • Which is normally disabled and is a security risk in case you did not run enough sanity checks on your code. – AnrDaemon Oct 05 '22 at 07:17
1
UPDATE Table1 SET col1= col2 FROM (SELECT col2, col3 FROM Table2) as newTbl WHERE col4= col3

Here col4 & col1 are in Table1. col2 & col3 are in Table2
I Am trying to update each col1 where col4 = col3 different value for each row

Jaimil Patel
  • 1,301
  • 6
  • 13
ankit giri
  • 388
  • 3
  • 11
-8

I did it this way:

<update id="updateSettings" parameterType="PushSettings">
    <foreach collection="settings" item="setting">
        UPDATE push_setting SET status = #{setting.status}
        WHERE type = #{setting.type} AND user_id = #{userId};
    </foreach>
</update>

where PushSettings is

public class PushSettings {

    private List<PushSetting> settings;
    private String userId;
}

it works fine

ru51an
  • 113
  • 1
  • 1
  • 10