EDIT: This question title originally was: How does Doctrine know last inserted id in MySQL? and was related to Doctrine ORM mapper. After some digging I found out that this question is not related to Doctrine but to PDO_MySQL, MySQL C API and finally - to MySQL client-server communication. I have decided to change the title, so maybe someone will find answer to his/hers question.
For those who are not using Doctrine: I was curious, why bellow:
mysql_query("INSERT INTO category (name) VALUES('cat')");
echo mysql_insert_id();
or similar:
$pdo->exec("INSERT INTO category (name) VALUES('cat')");
echo $pdo->lastInsertId();
will lead to only one position (without separate SELECT LAST_INSERT_ID()) in log:
1701 Query INSERT INTO category (name) VALUES ('cat')
Original question:
I have 2 tables:
category(id,name)
product(id, name, categoryId)
I created new category object and product object. I assigned category object to product object. I didn't set any ids:
$product = new Product();
$product->name = 'asdf';
$category = new Category();
$category->name = 'cat';
$product->Category = $category;
After that I flushed the connection and check MySQL logs:
1684 Query START TRANSACTION
1684 Query INSERT INTO category (name) VALUES ('cat')
1684 Query INSERT INTO product (name, categoryid) VALUES ('asdf', '312')
1684 Query COMMIT
How did Doctrine know, that the newly created category id is 312? There is nothing else in logs.
I did some research and browse some source code, so my answer could be a little bit wrong and not precise.
First of all, this is not really related to Doctrine. Doctrine uses PDO_MYSQL. But internally PDO_MYSQL uses the same thing as mysql_insert_id function - native MySQL C API function - mysql_insert_id.
The reason why there is no seperate SELECT LAST_INSERT_ID() lies in the fact, that after the statement is executed (in my example INSERT), server responds with data and some other things included in OK Packet), including insert_id. So when we fire mysql_insert_id() we are not connecting to the server, to receive insert_id - mysql library does not need to do that - it already has this value stored from last execution of query (at least I think so after analyzing the file libmysql.c)
OK Packet is described here: MySQL Generic Response Packets - OK Packet
Probably by calling http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
last_insert_id works only for AUTO_INCREMENT columns. It will not work if you insert a value manually into the AUTO_INCREMENT column.
The doctrine will work because it is just like assigning a NULL to it (although you never specifically write that). This triggers the auto increment and provide a value for last_insert_id().
I have included a jpg here so that you can see it. Hope it helps!
Related
I am using redbeanphp 4.3 and struggling to understand why a mysql-db-trigger does not get triggered (or may be ignored).
When I work directly in the database (with phpmyadmin) and insert a row in table B, a column in table A gets updated.
When I work indrectly with the database (via a rest-api and therefore with redbean php), the row is inserted as well in table B, but the column in table A does not get updated.
This exact same behaviour I found in my android application with sugarorm too.
I then reasoned, that using an ORM ignores the usage of triggers. But so far I did not find any statement support my thesis, not in some forums, the redbean documentation nor in the rd.php source code.
An explanation or any help would be appreciated.
Thanks in advance.
Edit 1:
A script using mysqli and pdo-driver does result in the same behaviour as manual insertion (the column gets updated). Therefore PDO can not be the reason.
Solution:
Table A and B have an 1:N relation.
The problem was a misuse of redbean's R::store() method. The object1 of table A was loaded, an object2 of table B was inserted (by R::store()), the trigger was executed, but the object1 of table A did not recognize it. A final R::store() on object1 has overwritten the background updates to the initial state.
So the solution is quite simple: removing R::store() on objects of table B.
When R::store() is called on object1 of table A, it first updates entity A and inserts entity B afterwards, which will trigger the update of object1.
Try to debug the sql querys created by redBean and check if there is a column update or something else after your insert.
Configure redBean to log sql querys:
R::debug( TRUE, 3 );
Access and print the log:
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->getLogs();
print_r( $logs );
You will find more information about debugging redBean on their official site.
I have a table which has id, name ,surname columns. When I add a new line to table, id increases by 1 since its AI and PK. Now how I get back latest id variable with OUTPUT command?
"INSERT INTO table (name, surname) VALUES ('mike', 'hensen') OUTPUT ?????? how to continue ????"
edit LAST_INSERT_ID() is not a very good method since in a big webpage there could be a lot adding per second.
I'm pretty sure that LAST_INSERT_ID() is exactly what you want. It returns the last id inserted on a per connection basis, not the last one inserted (documented here). Presumably, different web users would have different connections, so using the function does what you want.
If you want the last id that was inserted over all connections, but not necessarily from your most recent insert, then you can look at the auto_increment value in the metadata.
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).
I'm still new and some of the right coding practices escape me. Documentation on this particular situation is weak, so I would like to get some advice/suggestions from you experts :) on the following.
I have an API that allows users to update 2 tables in one call. One is a SUMMARY table and the other is a DETAIL table with an FK to the SUMMARY table.
What I have my code doing is I do an UPSERT (insert/update) to the SUMMARY table, grab the insert_id and then delete the records from the DETAIL table, then insert the ones I need (referencing SUMMARY with the fk of course).
However, in the instance that there are no changes to SUMMARY data - insert_id returns 0. This seems expected as no row was updated/inserted.
So here is my question:
Should I be doing a full read of the tables and comparing data prior to this update/delete/insert attempt? Or is there another nifty way of grabbing the id of the SUMMARY that was a duplicate of the UPSERT attempt? I feel that my users will 'almost' ALWAYS be changing the SUMMARY and DETAIL data when using this API.
What is the correct coding practice here? Is the extra read worth it every time? Or should I read only if insert_id = 0?
Thoughts? My biggest problem is that I don't know what the magnitude difference of a read vs a write is here - especially since I don't believe the API will be called much without having changed values.
Again my options are:
Read db and compare to see if there is a diff
Insert/Update accordingly
Attempt Insert/update.
if (insert_id = 0) then read db to get summary id for details table
copmlete process
Attempt Insert/Update
use ?something? to get id of summary of record that was duplicate (and prevented insert/update)
use the id to complete steps.
If the id you need is an auto_increment field, option 4 (do everything inside DB with 1 execute action) 100% of the time. This is the general SQL structure you need:
Insert into summary (primaryKey, fieldA, fieldB) values (NULL, valueA, valueB) on duplicate key update primaryKey=LAST_INSERT_ID(primaryKey), fieldA = fieldA, fieldB=fieldB;
If you then do SELECT LAST_INSERT_ID() it'll give you either the successful inserted id or ,if duplicate, the duplicate entrie's id. So do something like:
delete from detail where summary_id = LAST_INSERT_ID();
At the companies I've worked for, option 1 is usually the one I've seen used if you're wanting to compare record by record. This can be implemented either in a stored proc or in the code itself. Depends on the context of what "same" means. If it's raw values, then sql is probably the easiest. If there's a context in addition to what the database has, you'll want to do it at the code level. Hope that helps.
I'm trying to keep the database tables for a project I'm working on nice and normalized, but I've run into a problem. I'm trying to figure out how I can insert a row into a table and then find out what the value of the auto_incremented id column was set to so that I can insert additional data into another table. I know there are functions such as mysql_insert_id which "get the ID generated from the previous INSERT operation". However, if I'm not mistaken mysql_insert_id just returns the ID of the very last operation. So, if the site has enough traffic this wouldn't necessarily return the ID of the query you want since another query could have been run between when you inserted the row and look for the ID. Is this understanding of mysql_insert_id correct? Any suggestions on how to do this are greatly appreciated. Thanks.
LAST_INSERT_ID() has session scope.
It will return the identity value inserted in the current session.
If you don't insert any rows between INSERT and LAST_INSERT_ID, then it will work all right.
Note though that for multiple value inserts, it will return the identity of the first row inserted, not the last one:
INSERT
INTO mytable (identity_column)
VALUES (NULL)
SELECT LAST_INSERT_ID()
--
1
INSERT
INTO mytable (identity_column)
VALUES (NULL), (NULL)
/* This inserts rows 2 and 3 */
SELECT LAST_INSERT_ID()
--
2
/* But this returns 2, not 3 */
You could:
A. Assume that won't be a problem and use mysql_insert_id
or
B. Include a timestamp in the row and retrieve the last inserted ID before inserting into another table.
The general solution to this is to do one of two things:
Create a procedural query that does the insert and then retrieves the last inserted id (using, ie. LAST_INSERT_ID()) and returns it as output from the query.
Do the insert, do another insert where the id value is something like (select myid from table where somecolumnval='val')
2b. Or make the select explicit and standalone, and then do the other inserts using that value.
The disadvantage to the first is that you have to write a proc for each of these cases. The disadvantage to the second is that some db engines don't accept that, and it clutters your code, and can be slow if you have to do a where on multiple columns.
This assumes that there may be inserts between your calls that you have no control over. If you have explicit control, one of the other solutions above is probably better.