I am using PHP and MySQL.
Goal:
Insert a new row into a table with an auto-increment id column.
While inserting this new row, I would like to copy the new inserted row's auto-increment id # into another column in the same row/entry.
Upon creation, the new entry/row should have the auto-incremented id column and another column with the same number.
My biggest concern:
I need all of this to happen correctly and reliably, even while other transactions from other connections could be taking place on the server.
So Far
I know only a little about last_insert_id()... and I am afraid of being able to use this reliably for my needs...
Will I ever run into a situation where the auto-increment id # will have already incremented (due to some other insert query from another connection perhaps) and now I will not get the correct id #? (I did not quite fully understand what it means when the last_insert_id() is given to the client on a per-connection basis).
Will last_insert_id() play nice with transactions since they become undefined when the transaction is made to rollback? (If another transaction is rolled back and then I run this script immediately after, will my script return NULL for last_insert_id()?)
I do not understand mysql_insert_id(); how to use it and whether or not it will help me.
So far, I have thought about:
INSERT row with column set as last_insert_id();
INSERT row; UPDATE column with SELECT last_insert_id();
SELECT last_insert_id(); INSERT row with auto-increment column and column set as last_insert_id()+1
What happens when I insert a chosen value into the auto-increment column? Will the auto-increment generator start counting from the number I insert? What if I use a value that has been used before (but doesn't exist anymore) and there exists records with id # that come after that value?
Will table or row locking allow me to achieve my desired behavior?
What is the proper/correct way to do something like this?
"last_insert_id() is given to the client on a per-connection basis"
last_insert_id is client independent and will return the ID for the
last inserted row from that client, therefore you do not need to worry
about the case that a user on another connection transacts with the
database.
I still do not fully understand what that means...
For a basic SCENARIO:
INSERT row where id = 1;
SELECT last_insert_id(); outputs 1
Person A makes a connection to the db; SELECT last_insert_id(); outputs 1.
Person B makes a connection to the db; SELECT last_insert_id(); outputs 1?
Person A INSERT another row where id = 2;
Person A SELECT last_insert_id(); outputs 2?
Person B SELECT last_insert_id(); outputs... 1 or 2??
What happens here?
And a SCENARIO that really concerns me:
Person A makes a connection with the db;
Person A SELECT last_insert_id(); outputs 1
Person A INSERT row where id = 2;
Person A SELECT last_insert_id(); outputs 2
Person B makes a connection with the db;
Person B SELECT last_insert_id(); outputs 2
Person B INSERT row where id = 3;
Person A SELECT last_insert_id(); outputs 2??
Person B SELECT last_insert_id(); outputs 3??
In this case, Person A's last_insert_id() is one count behind.
If this is true, then I will not be able to use my #3 method.
Please correct my outputs for me wherever I may be wrong.
You should check out this article regarding MySQL last_insert_id
last_insert_id is client independent and will return the ID for the last inserted row from that client, therefore you do not need to worry about the case that a user on another connection transacts with the database.
considering you may have multiple access to your DB,
i will do:
1, just insert new row and put null into the "another column" let's call it [autoID_Copy]
2, then i can run this:
update table set autoID_Copy= autoID where autoID_Copy is null
even make it faster you may do
where timeInstered < 1 min or maybe some other filter.
hope this help.
Related
I have this query in php. It's an insert select copying from table2, but I need to get the IDs of the newly created rows and store them into an array. Here is my code:
$sql = "INSERT INTO table1 SELECT distinct * from table2";
$db->query($sql);
I could revert the flow starting with a select on table2 and making all single inserts but it would slow down the script on a big table. Ideas?
You could lock the table, insert the rows, and get the ID of the last item inserted, and then unlock; that way you know that the IDs will be contiguous as no other concurrent user could have changed them. Locking and unlocking is something you want to use with caution though.
An alternative approach could be to use one of the columns in the table - either an 'updated' datetime column, or an insert-id column (for which you put in a value that will be the same across all of your rows.)
That way you can do a subsequent SELECT of the IDs back out of the database matching either the updated time or your chosen insert ID.
I'm using PHP to insert groups of records into a MySQL DB.
Whenever I insert a group of records, I want to give that group a unique set ID that is incremented by 1 for each group of records in the DB.
Currently, I'm checking the latest set ID in the DB and incrementing it by 1 for each new set of records.
The thing that scares me though is what happens if I query the DB to get the latest set ID, and before I can insert a new set of records with that set ID + 1, another insert occurs on the table thus taking the set ID I was about to use?
While fairly unlikely, something like that could greatly sacrifice the integrity of the data.
What can I do to prevent such a thing from happening? Is there any way to temporarily lock the DB table so that no other inserts can occur until I have performed a SELECT/INSERT combo?
Locking the table is one option, but that approach impacts concurrency.
The approach I would recommend is that you use a separate table with AUTO_INCREMENT column, and use a separate INSERT into that table, and a SELECT LAST_INSERT_ID() to retrieve the auto_increment value.
And then use that value as the group identifier for the group of rows you insert into your original table.
The basic approach is:
LOCK TABLE foo WRITE;
SELECT MAX(id) + 1 FROM foo
INSERT ...
INSERT ...
UNLOCK TABLES;
Locking the table prevents any other process from changing the table until you explicitly unlock it.
Having said that, seriously consider just using a table with an AUTO_INCREMENT column. MySQL will do the work of maintaining unique keys wholly automatically, and then you can simply refer to those keys from your existing table.
I am using Laravel 3. I have a form, and 2 tables. One table has an AUTO INCREMENT ID and I want to give this ID to the second table. eg.
table1
ID: Auto increment
title:
text:
date:
table2
ID: Auto increment
t1_ID: table1->ID
text:
Is it possible somehow?
I think I can get the ID of the last record in table1 and add 1 to it in the controller, but maybe there is an easier way.
SOLUTION #1
This sounds like you need to use a trigger. To be safe you should use an AFTER INSERT trigger because you cannot predict if the autoincrement will increment more than once in the event of some unforeseen error during an INSERT.
Perhaps the trigger should look something like this:
DELIMITER $$
CREATE TRIGGER table1_ai AFTER INSERT ON table1 FOR EACH ROW
BEGIN
INSERT INTO table2 (t1_ID) VALUES (NEW.ID);
END; $$
DELIMITER ;
That way, the ID is safely placed in table2, knowing that the ID is in actual use in table1.
SOLUTION #2
You could retrieve the ID using the information_schema. How?
Suppose the table is mydb.table1. You can quickly get the next auto_increment value like this:
SELECT auto_increment FROM information_schema.tables
WHERE table_schema='mydb' and table_name='table1';
This could be risky to retrieve if mydb.table1 experiences high-volume writes
EPILOGUE
You should go with SOLUTION #1, since implementing the trigger would handle the ID's placement in two tables without you having to code it.
I am trying to write a single MySQL insert query that will identify the highest value in a column and then increment it by one for the record being inserted. I thought when I made the table that I had this field set to auto_increment but it does not work for some reason. My current insert statement is:
INSERT INTO victoria (name, album, order_by) VALUES (:name, :album, :order)
The order_by field is the one that needs to increment by one.
If you want, for some reason, to increment a value of a column without using an auto_increment column you can do something like this
INSERT INTO victoria
SELECT :name, :album,
COALESCE((SELECT MAX(order_by) FROM victoria), 0) + 1;
Note: it might fail to provide you with a distinct value under heavy load, meaning several concurrent users inserting rows at the same time can grab the same MAX(order_by) value. Therefore if you are not planning to "reorder" rows latter you better stick with an auto_increment column.
Here is SQLFiddle demo
I have a number which is in this form : 2012-01 (2012 as current year) and 01 is just a the maximum value of a field in my database incremented by 1, and each year that number is reset to 0.
but if there are two users that try to do the same operation at the same time the value is the same for both and thus i get the same number inserted twice in my database .
I thought of creating a sequence but that requires a job that resets the sequence each year and i would prefer if there is a way to make a lock before i get the next number and
release it after an insert is done ?
Thanks.
CREATE UNIQUE INDEX index_name ON table_name (column_name);
or
ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (column_name);
You don't specify where you store the field that is used as the counter. But maybe it is possible to use a SELECT FOR UPDATE statement.
Before you increment the value of your counter field by 1 you can lock that record by using a SELECT FOR UPDATE. Then update the counter.
Something like this, assuming the table has only 1 record:
SELECT *
FROM CounterTable
FOR UPDATE;
UPDATE CounterTable
SET Counter = Counter + 1;
COMMIT;
If one session (user) has done the SELECT FOR UPDATE and not yet committed or rolled back, the other session (user) doing a SELECT FOR UPDATE will block waiting to be able to get a lock. This prevents two users from getting the same number.