156

How do I get the ID of the last updated row in MySQL using PHP?

NikiC
  • 100,734
  • 37
  • 191
  • 225
Avinash
  • 6,064
  • 15
  • 62
  • 95
  • 2
    mysqli_insert_id($link) will return exactly the id of last updated row. I am using this funtion in my projects for the same purpose. You can use it both in synchronous or asynchronous queries. Just insert $lastupdated_row_id = mysqli_insert_id($link) into your code and it will work for you. – Naeem Ul Wahhab Feb 05 '13 at 06:23
  • 1
    Don't you already know the ID of the row if you update ? I guess there must be some cases where you don't. – jlcharette May 27 '14 at 13:42
  • 1
    You probably use a where clause in your update query, why can't you use the same where clause in the select query? `UPDATE foo WHERE bar = 1; SELECT id FROM foo WHERE bar = 1`? – Salman A Aug 07 '15 at 17:05

12 Answers12

270

I've found an answer to this problem :)

SET @update_id := 0;
UPDATE some_table SET column_name = 'value', id = (SELECT @update_id := id)
WHERE some_other_column = 'blah' LIMIT 1; 
SELECT @update_id;

EDIT by aefxx

This technique can be further expanded to retrieve the ID of every row affected by an update statement:

SET @uids := null;
UPDATE footable
   SET foo = 'bar'
 WHERE fooid > 5
   AND ( SELECT @uids := CONCAT_WS(',', fooid, @uids) );
SELECT @uids;

This will return a string with all the IDs concatenated by a comma.

Ε Г И І И О
  • 11,199
  • 1
  • 48
  • 63
Pomyk
  • 3,288
  • 1
  • 17
  • 8
  • 7
    Can't believe this had 0 upvotes, and the mysql_insert_id() comment which is not what the OP was asking at all has 13. Thanks Pomyk, a clever trick! – Jaka Jančar Jan 11 '11 at 15:47
  • 1
    Since you have a `WHERE` clause you can simply use the same `WHERE` clause in your next query `SELECT id FROM footable WHERE fooid > 5` – Salman A Jul 15 '11 at 07:08
  • 4
    Maybe everyone understands this, but if you use mysql_query(); you have to divide this into three different calls, but it will work. – user254875486 May 15 '12 at 10:15
  • @Lex - the improved mysql extension allows multiple statements: http://www.php.net/manual/en/mysqli.multi-query.php – zupa Nov 29 '12 at 10:27
  • 1
    I change the code first line to `SET @uids := '';` in order to work, otherwise the result of `SELECT @uids;` will be `blob`. – Hendry H. Jul 01 '13 at 10:04
  • 8
    There's no need to even use variables. `LAST_INSERT_ID()` can accept an argument which will set the value returned by the next call to `LAST_INSERT_ID()`. For example: `UPDATE table SET id=LAST_INSERT_ID(id), row='value' WHERE other_row='blah';`. Now, `SELECT LAST_INSERT_ID();` will return the updated id. See newtover's answer for more details. – Joel Jun 02 '14 at 14:35
  • Does the SET cater for when the UPDATE affects no rows? Seems to work without it for me - but I am guessing that's the use case, otherwise the var ends up undefined? – Steve Jun 05 '14 at 16:14
  • The answer provided by 'newtover' is the correct answer. Using `LAST_INSERT_ID()` is exactly what the MySQL Documentation says to do. – Timothy Zorn Sep 08 '14 at 00:23
  • 1
    Just watch out that with LAST_INSERT_ID it can return a value even if the row wasn't updated (i.e. from the previous row inserted / updated if you use the above query), so you'll need to check the number of affected rows from the update query first, if its 1, use LAST_INSERT_ID to get the ID of the row updated. – Steve Childs Jan 13 '15 at 17:00
  • 3
    @SteveChilds I was just commenting newtover's question, and want to add a solution to the pitfall you're describing. This sets LAST_INSERT_ID to 0 if there's nothing to UPDATE: `UPDATE lastinsertid_test SET row='value' WHERE row='asd' AND LAST_INSERT_ID(id) OR LAST_INSERT_ID(0);`. However it doesn't retrieve multiple ids, so this answer is superior. – martinczerwi Apr 29 '15 at 08:35
  • Boggles my mind that MySQL doesn't have some simple LAST_UPDATE_ID to avoid this – AlxVallejo Jan 16 '17 at 02:47
  • I don't understand how this would work with an autonumber PK... you can't update those, so why would this statement work? – Joe Phillips Apr 27 '18 at 20:13
  • This is not about updating id, but getting the id of updated rows. – Pomyk May 15 '18 at 21:19
  • I tried to make the solution for the multiple updated rows work inside a stored procedure. Instead of a User-defined Variable I used a Local Variable (using DECLARE) but the SELECT inside the WHERE does not seem to be working so I believe the WHERE expression evaluates to false and that is why the update fails and the variable value stays NULL. I also tried SELECT INTO inside the WHERE condition but this produces an error. Any ideas how to solve this? Please help – retset Nov 02 '18 at 02:33
37

Hm, I am surprised that among the answers I do not see the easiest solution.

Suppose, item_id is an integer identity column in items table and you update rows with the following statement:

UPDATE items
SET qwe = 'qwe'
WHERE asd = 'asd';

Then, to know the latest affected row right after the statement, you should slightly update the statement into the following:

UPDATE items
SET qwe = 'qwe',
    item_id=LAST_INSERT_ID(item_id)
WHERE asd = 'asd';
SELECT LAST_INSERT_ID();

If you need to update only really changed row, you would need to add a conditional update of the item_id through the LAST_INSERT_ID checking if the data is going to change in the row.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
newtover
  • 31,286
  • 11
  • 84
  • 89
  • 5
    It is multi-user safe because multiple clients can issue the UPDATE statement and get their own sequence value with the SELECT statement (or mysql_insert_id()), without affecting or being affected by other clients that generate their own sequence values. As per https://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id – brutuscat Apr 21 '14 at 14:16
  • 1
    Won't this set the item_id to the last inserted record? – arleslie Aug 02 '14 at 00:03
  • 2
    @arleslie, if you follow the link to the docs in the brutuscat's comment above, you will see it won't. This is the intended behavior exactly for the case. – newtover Aug 04 '14 at 08:56
  • 2
    This is indeed the correct answer. Although the query seems a bit counter-intuitive, this is exactly what the MySQL Documentation says to do. – Timothy Zorn Sep 08 '14 at 00:21
  • It's correct, but limited to 64-bit numeric values (unsigned since 5.5.29, signed in earlier versions). – Pomyk Sep 08 '14 at 20:55
  • 3
    In case the query is confusing you, since LAST_INSERT_ID(expr) will return the value of expr for the UPDATE call you could also use it like this: `UPDATE items SET qwe = 'qwe' WHERE asd = 'asd' AND LAST_INSERT_ID(item_id); SELECT LAST_INSERT_ID();` – martinczerwi Apr 29 '15 at 08:10
  • It is also worthy to add that if you need to get the value of a field that is not the primary key you can also use that statement to get that value `UPDATE items SET qwe = 'qwe' WHERE asd = 'asd' AND LAST_INSERT_ID(some_other_column); SELECT LAST_INSERT_ID();` – Cesar Bielich May 04 '18 at 20:48
  • This is far from a clean solution. It is hard to see what it does. It only works with integer values, but even the primary key is not necessarily an integer. Also, don't put it to the `where` clause - it would work if it is the last condition and there is no `or` involved, but you could easily get unexpected results. – maf-soft Apr 13 '21 at 15:31
21

This is officially simple but remarkably counter-intuitive. If you're doing:

update users set status = 'processing' where status = 'pending'
limit 1

Change it to this:

update users set status = 'processing' where status = 'pending'
and last_insert_id(user_id) 
limit 1

The addition of last_insert_id(user_id) in the where clause is telling MySQL to set its internal variable to the ID of the found row. When you pass a value to last_insert_id(expr) like this, it ends up returning that value, which in the case of IDs like here is always a positive integer and therefore always evaluates to true, never interfering with the where clause. This only works if some row was actually found, so remember to check affected rows. You can then get the ID in multiple ways.

MySQL last_insert_id()

You can generate sequences without calling LAST_INSERT_ID(), but the utility of using the function this way is that the ID value is maintained in the server as the last automatically generated value. It is multi-user safe because multiple clients can issue the UPDATE statement and get their own sequence value with the SELECT statement (or mysql_insert_id()), without affecting or being affected by other clients that generate their own sequence values.

MySQL mysql_insert_id()

Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement. Use this function after you have performed an INSERT statement into a table that contains an AUTO_INCREMENT field, or have used INSERT or UPDATE to set a column value with LAST_INSERT_ID(expr).

The reason for the differences between LAST_INSERT_ID() and mysql_insert_id() is that LAST_INSERT_ID() is made easy to use in scripts while mysql_insert_id() tries to provide more exact information about what happens to the AUTO_INCREMENT column.

PHP mysqli_insert_id()

Performing an INSERT or UPDATE statement using the LAST_INSERT_ID() function will also modify the value returned by the mysqli_insert_id() function.

Putting it all together:

$affected_rows = DB::getAffectedRows("
    update users set status = 'processing' 
    where status = 'pending' and last_insert_id(user_id) 
    limit 1"
);
if ($affected_rows) {
    $user_id = DB::getInsertId();
}

(FYI that DB class is here.)

Val Kornea
  • 4,469
  • 3
  • 40
  • 41
  • Important to note: this is multi-connection safe, the persisted value for last_insert_id or mysql_insert_id (and maybe mysqli_insert_id, I didn't check), are per-connection. Awesome! – Ryan S May 09 '18 at 02:33
  • This looks like a good solution, but introduced a bug that I still don't understand - when processing multiple entries, the amount of returned IDs were skipped occasionally, which is a different behavior from the original query that was a derived merge (select statement inside update statement). I had to revert this and do two different queries instead. – Lucas Bustamante Nov 12 '21 at 21:36
  • @LucasBustamante what is your environment? node-mysql2 connection pooling breaks transactions, temporary tables, and last_insert_id(). The workaround is to extract a connection from the pool, run consecutive queries on the same connection, and then release the connection back to the pool. – Val Kornea Nov 12 '21 at 23:53
  • @RyanS Per-connection or per session? – TheRealChx101 Apr 14 '23 at 11:09
  • @TheRealChx101 oh gosh this was 5 years ago sorry :D I think I was reading this doc: https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id it says "per-connection" and "each client" - beyond that, I'm not sure sorry! – Ryan S Apr 15 '23 at 04:15
11

This is the same method as Salman A's answer, but here's the code you actually need to do it.

First, edit your table so that it will automatically keep track of whenever a row is modified. Remove the last line if you only want to know when a row was initially inserted.

ALTER TABLE mytable
ADD lastmodified TIMESTAMP 
    DEFAULT CURRENT_TIMESTAMP 
    ON UPDATE CURRENT_TIMESTAMP;

Then, to find out the last updated row, you can use this code.

SELECT id FROM mytable ORDER BY lastmodified DESC LIMIT 1;

This code is all lifted from MySQL vs PostgreSQL: Adding a 'Last Modified Time' Column to a Table and MySQL Manual: Sorting Rows. I just assembled it.

Salman A
  • 262,204
  • 82
  • 430
  • 521
Chad von Nau
  • 4,316
  • 1
  • 23
  • 34
  • 7
    This method may fetch the wrong id if the second insert is done just after the first insert query. However If we use 'WHERE' with this query i.e. SELECT id FROM mytable ORDER BY lastmodified DESC LIMIT 1 WHERE (some conditions related to last insert query), then It may prevent fetching the wrong id. – Naeem Ul Wahhab Jan 30 '13 at 15:14
  • 1
    @TheNoble-Coder This is true. If you need to get the id of a row affected a particular update query, then use Pomyk's technique. This technique here is more useful when used asynchronously, where you just want the absolute last update. – Chad von Nau Feb 02 '13 at 20:03
7

Query :

$sqlQuery = "UPDATE 
            update_table 
        SET 
            set_name = 'value' 
        WHERE 
            where_name = 'name'
        LIMIT 1;";

PHP function:

function updateAndGetId($sqlQuery)
{
    mysql_query(str_replace("SET", "SET id = LAST_INSERT_ID(id),", $sqlQuery));
    return mysql_insert_id();
}

It's work for me ;)

Gigoland
  • 1,287
  • 13
  • 10
  • i liked same but ran differntly ref : http://forums.mysql.com/read.php?52,390616,390619 –  Aug 16 '15 at 14:40
6
SET @uids := "";
UPDATE myf___ingtable
   SET id = id
   WHERE id < 5
  AND ( SELECT @uids := CONCAT_WS(',', CAST(id AS CHAR CHARACTER SET utf8), @uids) );
SELECT @uids;

I had to CAST the id (dunno why)... or I cannot get the @uids content (it was a blob) Btw many thanks for Pomyk answer!

Gromish
  • 189
  • 2
  • 9
3

ID of the last updated row is the same ID that you use in the 'updateQuery' to found & update that row. So, just save(call) that ID on anyway you want.

last_insert_id() depends of the AUTO_INCREMENT, but the last updated ID not.

Sebas
  • 21,192
  • 9
  • 55
  • 109
Driada
  • 47
  • 2
3

Hey, I just needed such a trick - I solved it in a different way, maybe it'll work for you. Note this is not a scalable solution and will be very bad for large data sets.

Split your query into two parts -

first, select the ids of the rows you want to update and store them in a temporary table.

secondly, do the original update with the condition in the update statement changed to where id in temp_table.

And to ensure concurrency, you need to lock the table before this two steps and then release the lock at the end.

Again, this works for me, for a query which ends with limit 1, so I don't even use a temp table, but instead simply a variable to store the result of the first select.

I prefer this method since I know I will always update only one row, and the code is straightforward.

olamundo
  • 23,991
  • 34
  • 108
  • 149
3

My solution is , first decide the "id" ( @uids ) with select command and after update this id with @uids .

SET @uids := (SELECT id FROM table WHERE some = 0 LIMIT 1);
UPDATE table SET col = 1 WHERE id = @uids;SELECT @uids;

it worked on my project.

Ahmet Uğur
  • 462
  • 6
  • 12
3

Further more to the Above Accepted Answer
For those who were wondering about := & =

Significant difference between := and =, and that is that := works as a variable-assignment operator everywhere, while = only works that way in SET statements, and is a comparison operator everywhere else.

So SELECT @var = 1 + 1; will leave @var unchanged and return a boolean (1 or 0 depending on the current value of @var), while SELECT @var := 1 + 1; will change @var to 2, and return 2. [Source]

Community
  • 1
  • 1
Anon30
  • 567
  • 1
  • 6
  • 21
  • Hey, thanks. What they mean is actually more interesting than the accepted answer above. – Green Jul 21 '17 at 12:42
2

If you are only doing insertions, and want one from the same session, do as per peirix's answer. If you are doing modifications, you will need to modify your database schema to store which entry was most recently updated.

If you want the id from the last modification, which may have been from a different session (i.e. not the one that was just done by the PHP code running at present, but one done in response to a different request), you can add a TIMESTAMP column to your table called last_modified (see http://dev.mysql.com/doc/refman/5.1/en/datetime.html for information), and then when you update, set last_modified=CURRENT_TIME.

Having set this, you can then use a query like: SELECT id FROM table ORDER BY last_modified DESC LIMIT 1; to get the most recently modified row.

a1kmm
  • 1,354
  • 7
  • 13
-11

No need for so long Mysql code. In PHP, query should look something like this:

$updateQuery = mysql_query("UPDATE table_name SET row='value' WHERE id='$id'") or die ('Error');
$lastUpdatedId = mysql_insert_id();
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
Driada
  • 3
  • 2