I want to get last inserted row id in mysql any one help me
Table field is
If you want to get the id just after the insertion, use LAST_INSERT_ID():
SELECT LAST_INSERT_ID();
This will return the AUTO_INCREMENT value of the last inserted element (in your current connection).
If you want to know which is the last inserted value in a determined table, use any of the queries provided in the other answers.
You can use LAST_INSERT_ID(), but you should be aware that (you not only should have the AUTO_INCREMENENT), but it does operates in at connection level. This is, it will return the last_insert_id() for the current connection and not the last_insert_id() that another connection --that might be happening meanwhile-- generated.
Example (lets say the operations arrive the database in the following order):
-Connection A : user A -> insert order (id = 1000), lets say $20
-Connection B : user B -> insert order (id = 1001), lets say $50
.... multiple orders from other users are placed
-Connection B : you retrieve last_insert_id (value 1001) and you retrieve the order amount, and update the user B profile with the total revenue generated (+$50).
-Connection A : you retrieve last_insert_id (value 1000) and you retrieve the order amount, and update the user A profile with the total revenue generated (+$20).
You should be aware that last-insert_id() operates on a connection level, therefore you want to keep the connection open until you are finished. You should not use things like do a max on the database, because on a web environment you don't have control on how many users will use your app at same time, and in which order the operations will be executed and most important you want to keep your database consistent.
Related
Short version
Would someone provide an example of this? There are 3 SQL tables. Using INSERT ... SELECT, take data from table 1 and insert it into table 2. Then, INSERT rows into table 3, using the auto-increment id of each table 2 row just inserted using that INSERT ... SELECT statement.
INSERT ... SELECT creates multiple rows but you cannot obtain the auto-increment ID from them, for use in a subsequent INSERT statement.
Expanded version
I'm looking for an efficient way to use the auto increment IDs, created from an INSERT ... SELECT, in a second INSERT.
Imagine this scenario in a warehouse.
The warehouse receives a pallet of goods from a supplier. The pallet contains multiple individual items, which must be dispatched to different customers. The pallet is booked in, broken down and checked. Each item is then assigned to the correct customer and marked as "ready". At this point, each item is dispatched with the dispatch status recorded per customer. Each Customer's account balance is reduced by a given value based on the item.
The issue is linking the account reduction to the item dispatch. There are 3 tables:
GoodsIn: records the pallet arrival from the supplier
CREATE TABLE GoodsIn ('InID' 'CustomerID', 'ItemSKU_ID', 'HasBeenChecked')
GoodsOut: records the SKU dispatch to the Customer
CREATE TABLE GoodsOut ('OutID', 'CustomerID', 'ItemSKU_ID', 'DateDispatched')
Account: records each Customer transaction/balance
CREATE TABLE Ledger ('LedgerID', 'BalanceClose', 'AdjustmentAmount', 'CustomerID', 'ActionID')
(I've massively simplified this - please accept that GoodsIn and GoodsOut cannot be combined)
When an SKU is marked as ready for dispatch, I can use the following to automatically update the Ledger balance, taking the last balance row per customer and updating it
INSERT INTO Ledger (BalanceClose, AdjustmentAmount, CustomerID)
SELECT Ledger.BalanceClose +
(SELECT #Price:=ItemSKUData.ItemPrice FROM ItemSKUData WHERE ItemSKUData.ItemSKU_ID = GoodsIn.ItemSKU_ID) AS NEWBALANCECLOSE,
#Price AS ADJUSTMENTAMOUNT,
Ledger.CustomerID
FROM Ledger
INNER JOIN GoodsIn ON GoodsIn.CustomerID = Ledger.CustomerID
WHERE GoodsIn.HasBeenChecked = TRUE
AND Ledger.LedgerID IN (SELECT MAX(Ledger.LedgerID) FROM Ledger GROUP BY Ledger.CustomerID)
This all works absolutely fine - I get a new Ledger row, with the updated BalanceClose, for each GoodsIn row where GoodsIn.HasBeenChecked = TRUE. Each of these Ledger rows gets an auto-increment Ledger.LedgerID on INSERT.
I can then do pretty much the same code to INSERT into the GoodsOut table. Again as with Ledger, GoodsOut.OutID is an auto-increment ID.
I now need to link those Ledger rows (Ledger.ActionID) to the GoodsOut.OutID. This is the purpose of Ledger.ActionID - it needs to map to each GoodsOut.OutID, so that the reduction of the Ledger balance is linked to the action of sending the goods to the customer.
In theory, if this was a single INSERT and not an INSERT SELECT, I would simply take the GoodsOut.LAST_INSERT_ID() and use it on the INSERT INTO Ledger.
But because I'm using an INSERT ... SELECT, I can't get the auto-increment ID of each row.
The only way I can see to do this is to use a dummy column in the GoodsOut table, and store the GoodsIn.InID in it. I could then get the GoodsOut.OutID using a WHERE in the INSERT ... SELECT for the Ledger.
It doesn't feel very elegant and safe though.
So this is my question. I need to link table A to table B using table B's auto-increment ID, when all rows in BOTH table A and table B are created using INSERT ... SELECT.
You're right, when you do INSERT...SELECT for batch inserts, you don't have easy access to the auto-increment id. LAST_INSERT_ID() returns only the first id generated.
One documented behavior of bulk inserts is that the id's generated are guaranteed to be consecutive, because bulk inserts lock the table until the end of the statement.
https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html says:
innodb_autoinc_lock_mode = 1 (“consecutive” lock mode)
This is the default lock mode. In this mode, “bulk inserts” use the special AUTO-INC table-level lock and hold it until the end of the statement. This applies to all INSERT ... SELECT, REPLACE ... SELECT, and LOAD DATA statements. Only one statement holding the AUTO-INC lock can execute at a time.
This means if you know the first value generated, and the number of rows inserted (which you should be able to get from ROW_COUNT()), and the order of rows inserted, then you can reliably know all the id's generated.
The MySQL JDBC driver relies on this, for example. When you do a bulk insert, the full list of id's generated is not returned to the client (that is, the JDBC driver), but the driver has a Java method to return the full list. This is accomplished by Java code inferring the values, and assuming they are consecutive.
If i have insert query for example:
INSERT INTO user(username) VALUES('admin');
And then get the id of the inserted record with
LAST_INSERT_ID();
Looks find but what happens if between the insert and LAST_INSERT_ID() another insert is executed.
How MySQL knows to return the correct id (for the first or second insert) since no parameter is passed to LAST_INSERT_ID();
Is it save to use this function?
Thanks
I'm supposing that you mean what happen if i'm connected to the MySQL server and executing an INSERT but others are also doing insert, like updating a table on a website while client are currently using it.
If you go take a look at the documentation https://dev.mysql.com/doc/refman/8.0/en/information-functions.html there is a point that answers your questions:
The ID that was generated is maintained in the server on a
per-connection basis. This means that the value returned by the
function to a given client is the first AUTO_INCREMENT value generated
for most recent statement affecting an AUTO_INCREMENT column by that
client. This value cannot be affected by other clients, even if they
generate AUTO_INCREMENT values of their own. This behavior ensures
that each client can retrieve its own ID without concern for the
activity of other clients, and without the need for locks or
transactions.
This should be the same in MariaDB.
As discussed in the comment, you are wondering if you can use this in a php PDO environment. If you mean to use this directly from the database, it's a no, you won't be able to have the last inserted ID because you won't have the same client connection as PDO. If you want to use it directly from PDO please use the specific PDO function: http://php.net/manual/fr/pdo.lastinsertid.php , this should allow to do what you want.
If you insert multiple rows into a table using a single INSERT query, the LAST_INSERT_ID function returns the last insert id of the first row.
i.e
If your table id has 3 as column value and you will insert 4 rows in a single query then LAST_INSERT_ID will give you 4 instead of 7
If you insert 4 rows in 4 different insert query then LAST_INSERT_ID will give you 7
last_insert_id( ) Or mysqli_insert_id( ) will always return the id of last or most recent query. And also be noted that id field must have AUTO_INCREMENT enabled.
It doesn't give you the freedom to choose any specific table. Or you can't have id which is generated from your previous query.
So, from this point it serves a very small purpose. Only the last id, it doesn't matter which table.
To get last id from any specific table this query would be helpful : "SELECT id FROM table_name ORDER BY id DESC LIMIT 1"
I have a column with sets of numbers like 21,8,0,345,... if I wanted to +1 to only one element in the set, for example element 3, would I be able to do that with a mysql query?
I know I can do this with php code (explode the string to an array then update it) but I'm afraid that with multiple updates simultaneously on the same row the values will be rewritten.
First query will set 21,8,1,345 and the second will rewrite it with 21,9,0,345
Replacing the element in question might also not work because some rows have multiple elements with the same value like 2,40,40,41
MySQL supports "SELECT ... FOR UPDATE" specifically for this situation to make sure the row isn't overwritten while you're processing the the row contents.
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
The above link even gives a very similar example (except for exploding the elements, increasing the one you want, and imploding them back together).
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
The better answer, as Tim suggested, is to store this data in a separate table, especially since you have a variable number of items for each row. I don't know how you currently know that you want to update, say, the 3rd item but I'll assume that's known.
Let's say these numbers are temperature readings from various sensors at a "location" and they gradually go up and down. Your main table is "locations" with with fields:
id (int, auto-increment), location_name (varchar), ...
You're then going to create a new table called "readings" with fields:
id (int, auto-increment), location_id (int), temperature (smallint)
The "id" from the first table is going to match up to the "location_id" of many records in "readings".
When you want to add a new temperature reading to a location (I'm assuming you'll have a $location_id and $new_reading variables in PHP):
INSERT INTO readings (location_id, temperature)
VALUES ( $location_id, $new_reading )
(NOTE: You should be properly sanitizing your inputs, using PDO, or other library, but that's out of scope for this answer or I'm going to be here all night. :-) )
Let's say you want to update the 3rd reading for this location, that would mean the "offset" is 2 and you only want to update 1 record so that's what "LIMIT 2, 1" means below. (I tried and failed to find a way to do this in only 1 query; UPDATE does not seem to support offsets, at least not in my version of MySQL.)
SELECT id FROM readings WHERE location_id = 1 ORDER BY id LIMIT 2, 1;
/* Let's say you stored the above result in $reading_id */
UPDATE readings SET temperature = temperature + 1 WHERE id = $reading_id;
My question is more of a high level question than technology specific.
The problem I have is I am inserting into a database a list of users corresponding to a machine. When I do so I have no idea what users were previously but I want to remove users who are in the database that correspond to that machine but no longer are current.
The list that I have is the current list of users for that machine. Any ideas?
Solution 1:
Query DB to get a list of users corresponding to a machine.
Compare against current list and build a remove list and an add list.
Execute Remove
Execute Add
Drawbacks to this solution is that we execute 3 store procedure statements.
Positive is we don't have to change the database.
Solution 2:
Add a user timestamp field to the table.
Run add all users while updating the timestamp field.
Execute new stored procedure to remove all users corresponding
to machine whose updated is less than now.
Drawback is we add a field to each row.
Positive is this is likely only 2 queries.
the easiest solution from my perspective is to alter the table so that you add a datetime or timestamp that defaults to the current time, and on update updates to the current time. That way, when the table is altered this update occurs. Then build a job to run every X period (daily, weekly, monthly, etc) that removes users who have not been updated in some period.
This gives you the most control over your database, and the most accurate data after a short period.
Keep it simple. Why not:
-- Add any new machines
INSERT [Table] (MACHINE, [USER])
SELECT MACHINE, [USER] FROM Query Q WHERE NOT EXISTS(SELECT * FROM [Table] T WHERE T.MACHINE = Q.MACHINE)
-- Delete any removed machines
DELETE [Table] WHERE Machine NOT IN (SELECT Machine FROM Query)
-- Update any changed machines
UPDATE
A
SET
A.CoName = B.CoName
--SELECT *
FROM
Cust A
JOIN (SELECT CustID, CoName FROM Cust) B
ON A.CustID = B.CustID AND A.CoName <> B.CoName
Updated my answer to cover new machines and removed machines. Wrap it all up in a Stored Procedure.
Lets asume a table like:
ID = INT, AutoIncrement
VAR = VARCHAR, 65
FOO = VARCHAR, 65
I want to insert something new, and get the Auto-Generated ID, for further use.
My current solution is:
1.INSERT into table (VAR,FOO) VALUES ('test','anothertest')
2.SELECT * FROM table ORDER BY ID LIMIT 1
To get the last insert, but the problem is, what happends if the website lags, and there is a time gap between the queries?
example:
12:00AM 0.000s -> "INSERT into table (VAR,FOO) VALUES ('test','anothertest')
12:00AM 0.500s -> "INSERT into table (VAR,FOO) VALUES ('xyz', '!!!!!)`
12:00AM 0.800s -> "SELECT * FROM table ORDER BY ID LIMIT 1
the Query in 3 would not return the ID from 1, it would return the ID from 2
My Question no is, is there an absolute secure way to get a Value from a inserted Query?
like a confirmation "test, anothertest has successfully inserted into table, ID is 20"
the ID should be available as variable in php, just for information
Since you mention PHP there is usually a specific function to get that. If you are using the mysqli drivers then see mysqli_insert_id
Edit: According to the docs linked above The value of the AUTO_INCREMENT field that was updated by the previous query. Returns zero if there was no previous query on the connection or if the query did not update an AUTO_INCREMENT value. Since it is talking about queries on the connection then I would interpret that as meaning that queries on other connection (i.e. queries from other requests) won't affect the value returned. As long as you call it dirctly after the insert (before you do anythign else) then it should work.
Caveat: I am simply interpreting the docs here. I haven't actually tested for other calls myself. Wouldn't be that difficult though - simply have a script which does an insert, a sleep then fetches the ID, giving you time to do another insert during the sleep.
In php I use mysql_insert_id();
In later MySql insert/update I use LAST_INSERT_ID()
It's absolutely the same.
Try using, Scope_Identity(). This returns the last autogenerated id.
This link has more info: http://msdn.microsoft.com/en-us/library/ms190315.aspx