Php - Insert Autoincrement Value - For Parent/Child Tables - Concurrency Problem - php

Consider a simple schema of one to many relationship. Parent Table's Id is referenced in the Child table.
In php I want to insert a row into the table using the statement mysql_query($query). Then I will get the id of the last inserted row by using mysql_insert_id(). Then i will use this id to insert the another row into the child's table.
My question is that the since there could be multiple requests happening on the same time for a php page, what if the above two statements do NOT run one after the other (ie, for example, there are two inserts happening on the parent and then the two inserts on the child)? There could be concurrency issues. So how do we overcome this?
Any ideas?

When you call mysql_insert_id() it gets the last inserted id for that connection, so two PHP scripts won't interfere with each other.

As in MySQL documentation:
The value of mysql_insert_id() is
affected only by statements issued
within the current client connection.
It is not affected by statements
issued by other clients.
So... no problem. Although, for other reasons, I recommend using ORM (like Doctrine).

Related

Is it possible for stacked queries to sneak in between eachother when done by multiple users in short time windows?

I find this kind of hard to explain, but consider the following situation:
You have a website with two insert queries that get loaded right after eachother, there might be some variable declarations and for loops in between, but no other queries besides these two:
$mysqli->doQuery("INSERT INTO `company_order`(`customer_id`, `item_id`)
VALUES ($givenid, $givenproduct")
// This table has a primary key that gets defined using auto_increment.
/* (loop that defines array with 50~ variables, few names that get defined in object variables) */
$mysqli->doQuery("INSERT INTO `customer_orderlist`(`customer_id`,`order_id`)
VALUES ($givenid, (LAST_INSERT_ID()) ")
Imagine if two users loaded the same function that executes these queries almost right after eachother. Is there a risk that one user might get the last inserted ID from the other user, or is it guaranteed that the queries will be executed in order without any other queries getting called in between them?
The MySQL function LAST_INSERT_ID() returns the first auto-generated value successfully inserted for an AUTO INCREMENT column as a result of the most recent successful INSERT statement executed on the current connection.
The value is stored by the server on a per-connection basis. This guarantees the value returned by LAST_INSERT_ID() on your second query is the value generated for the AUTO INCREMENT column on your first query, no matter how many other INSERT queries run between these two queries on other connections.
To answer your questions:
is it guaranteed that the queries will be executed in order without any other queries getting called in between them?
No, there is no such guarantee. The queries are executed in the order you send them to the server but your connection does not block other connections to execute their own queries (and vice-versa).
Is there a risk that one user might get the last inserted ID from the other user
No. There is no such risk as long as the two queries (the INSERT that auto generates an AUTO INCREMENT value and the query that calls LAST_INSERT_ID()) run on the same connection (and no other INSERT query runs between them on the same connection).

How to add values simultaneously to the two related tables in MySQL?

I have this two tables:
I also have a dynamic form in which it contains table and the user can add rows and the data from it will be inserted in tblcamealsformdetails but the basis for inserting it is the id from tblcamealsform. How do I insert all the values to the two tables simultaneously?
Here's my form:
You will enter data first in table tblcamealsform. You insert ID from that query.
That ID you will use then to insert the rest of the data, along with the insert ID, in table tblcamealsformdetails.
So you don't do it simultaniously. You add the dependencies first.
To get the insert-id from the last query you executed, you will need mysql_insert_id().
See http://php.net/manual/en/function.mysql-insert-id.php
In answer to the comment what will happen if multiple users use the form at the same time:
Since you open a mysql connection at the top of your script which will result a unique connection pointer and all of the mysql-functions you call automatically reference to that pointer I think mysql_insert_id() will always reference to the latest query performed by the current connection. So another thread by another user would not interfere with this.
Please note that I am not 100% sure about this though.
Anyway: I am using this for about 10 years now some of which include high-traffic websites and I have never experienced any problems using this method, so in my opinion you can safely use it.
There is one exception to this:
You must always call mysql_insert_id() immediately after executing the query you want the ID for. If you execute any other query in the meantime (for example, you call a method of another object which performs an insert-query) mysql_insert_id() will return the ID of that query instead.
This is mistake I have made in the past and which you have to be aware of.
I'd like to point you using LAST_INSERT_ID:
when doing multiple-row inserts, LAST_INSERT_ID() will return the value of the first row inserted (not the last).

Updating related tables in MySQL

I need to update two tables in MySQL with PHP. The second table needs the ID of the row being inserted in the to first table.
At the moment I have some PHP code that loops through this process for each of the items in an array:
Check if record exists by attempting to get it's ID.
If the record doesn't exist insert it and get the last insert ID.
Update the second table using the ID we found as a foreign key.
This is very inefficient as multiple database calls are made. I would rather store the data in two arrays, one for each table, then batch insert them when the loop is done. The problem is I need to get the ID of the row in the first table before I can do this.
This is a problem I come across a lot. What is the most efficient / 'best practice' way of doing this?
Thank you
Create stored procedure for inserting whole hierarchy in one server call. Supply all parent-child records as XML and parse it/insert records inside procedure (afaik MySql should have XML-functions similar to MS SQL). This will result in the same number of INSERT statements however they will execute on server side which should improve performance. E.g.
exec MySp #myHierarchy = '<Recs><Parent Name="P1"><Child Name="C1" /><Child Name="C2"/></Parent></Recs>'

Tricky MySQL Batch Design

I have a scraper which visits many sites and finds upcoming events and another script which is actually supposed to put them in the database. Currently the inserting into the database is my bottleneck and I need a faster way to batch the queries than what I have now.
What makes this tricky is that a single event has data across three tables which have keys to each other. To insert a single event I insert the location or get the already existing id of that location, then insert the actual event text and other data or get the event id if it already exists (some are repeating weekly etc.), and finally insert the date with the location and event ids.
I can't use a REPLACE INTO because it will orphan older data with those same keys. I asked about this in Tricky MySQL Batch Query but if TLDR the outcome was I have to check which keys already exist, preallocate those that don't exist then make a single insert for each of the tables (i.e. do most of the work in php). That's great but the problem is that if more than one batch was processing at a time, they could both choose to preallocate the same keys then overwrite each other. Is there anyway around this because then I could go back to this solution? The batches have to be able to work in parallel.
What I have right now is that I simply turn off the indexing for the duration of the batch and insert each of the events separately but I need something faster. Any ideas would be helpful on this rather tricky problem. (The tables are InnoDB now... could transactions help solve any of this?)
I'd recommend starting with Mysql Lock Tables which you can use to prevent other sessions from writing to the tables whilst you insert your data.
For example you might do something similar to this
mysql_connect("localhost","root","password");
mysql_select_db("EventsDB");
mysql_query("LOCK TABLE events WRITE");
$firstEntryIndex = mysql_insert_id() + 1;
/*Do stuff*/
...
mysql_query("UNLOCK TABLES);
The above does two things. Firstly it locks the table preventing other sessions from writing to it until you the point where you're finished and the unlock statement is run. The second thing is the $firstEntryIndex; which is the first key value which will be used in any subsequent insert queries.

PHP Postgres: Get Last Insert ID

I found a couple of other questions on this topic. This one...
mysql_insert_id alternative for postgresql
...and the manual seem to indicate that you can call lastval() any time and it will work as expected. But this one...
Postgresql and PHP: is the currval a efficent way to retrieve the last row inserted id, in a multiuser application?
...seems to state that it has to be within a transaction. So my question is this: can I just wait as long as I like before querying for lastval() (without a transaction)? And is that reliable in the face of many concurrent connections?
INSERT, UPDATE and DELETE in PostgreSQL have a RETURNING clause which means you can do:
INSERT INTO ....
RETURNING id;
Then the query will return the value it inserted for id for each row inserted. Saves a roundtrip to the server.
Yes, the sequence functions provide multiuser-safe methods for obtaining successive sequence values from sequence objects.

Categories