Using OUTPUT-like clause after INSERT INTO in MySQL - php

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.

Related

how get last inserted id in multiple record insert?

I am trying to write a tag system which I want allow users to insert multiple tag in a text input. And then I want insert each tag in tag_table and need their id for keep in tag_post_table.
I know that in multiple insert last_insert_id return the first record id. Co can I add count of tags to last_insert_id for getting the real last id and use every id between them? Is this reliable?
I read this article, but I don't understand it completely
http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html
As I understand this article:
With innodb_autoinc_lock_mode set to 0 (“traditional”) or 1 (“consecutive”), the auto-increment values generated by any given statement will be consecutive, without gaps, because the table-level AUTO-INC lock is held until the end of the statement, and only one such statement can execute at a time.
Setting innodb_autoinc_lock_mode to 0 or 1 guarantees that the auto incemented ids are consecutive.
So the ID you get from LAST_INSERT_ID is the first of the new IDs and LAST_INSERT_ID + "the number of affected rows" is the last of the new IDs.
Also LAST_INSTERT_ID is not affected by other connections
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.
But notice, that there may be wrong results, if you use INSERT ... ON DUPLICATE KEY UPDATE or INSERT IGNORE .... But I have not tested this.

How to implement a global incrementing id for multiple tables in SQL?

I decided back when I was coding to have different tables for each type of content. Now I am stuck solving this. Basically my notification system ranks the newest content by its timestamp currently. This is inaccurate however because there is a small chance that someone would submit content at the same time as another person, and incorrect ranking would occur.
Now if I had all my content in a single table, I would simply rank it by an auto-incrementing variable. Is there a way to implement this auto-increment integer across multiple tables (e.g. When something is inserted into table1, id=0, something is inserted into table2, id=1). Or do I have to recode all my stuff into a single table.
NOTE:
The reason I have content in multiple tables is because its organized and it would reduce load stress. I don't really care about the organization anymore, because I can just access the data through a GUI I coded, I'm just wondering about the load stress.
EDIT:
I'm using PHP 5 with MySQL.
Your question, particularly the need for ID spanning over multiple tables, is clearly signalizing that your database design needs change. You should make one table for all content types (as a generalization), with autoincrementing ID. Then, for each particular content type, you can define other table (equivalent of inheritance in OOP) with extra fields, and foreign key pointing to the basic table.
In other words, you need something like inheritance in SQL.
You can create a table with auto increment id just to keep track of ids. Your program would do an insert on that table, get the id, use it as necessary.
Something along the lines of:
function getNextId() {
$res = mysql_query("INSERT INTO seq_table(id) VALUES (NULL)");
$id = mysql_insert_id();
if ($id % 10 == 0) {
mysql_query("DELETE FROM seq_table");
}
return $id;
}
Where seq_table is a table that you've to create just to get the ids. Make it a function so it can be used whenever you need. Every 10 ids generated I delete all generated ids, anyway you don't need them there. I don't delete every time since it would slow down. If another insert happen in the meantime and I delete 11 or more records, it doesn't affect the behaviour of this procedure. It's safe for the purpose it has to reach.
Even if the table is empty new ids will just keep on growing since you've declared id as auto-increment.
UPDATE: I want to clarify why the ID generation is not wrapped in a transaction and why it shouldn't.
If you generate an auto id and you rollback the transaction, the next auto id, will be incremented anyway. Excerpt from a MySQL bug report:
[...] this is not a bug but expected behavior that happens in every RDBMS we know. Generated values are not a part of transaction and they don't care about other statements.
Getting the ID with this procedure is perfectly thread safe. Your logic after the ID is obtained should be wrapped in a transaction, especially if you deal with multiple tables.
Getting a sequence in this way isn't a new concept, for instance, the code of metabase_mysql.php which is a stable DB access library has a method called GetSequenceNextValue() which is quite similar.
In a single table, you could have a field for the content type and clustered index that includes the content type field. This effectively keeps all of one content type in one place on the disc, and another content type in another place, etc. (It's actually organised into pages, but this physical organisation is still true.)
Assuming that each content type has the same fields, this would likely meet your needs and behave similarly to multiple tables. In some cases you may even find that, with appropriate indexes, a single table solution can be faster, more convenient and maintainable, etc. Such as trying to create global unique identifiers across all content types.
If you're unable to merge these back into a single table, you could create a central link table...
CREATE TABLE content_link (
id INT IDENTITY(1,1), -- MS SQL SERVER syntax
content_type INT,
content_id INT -- The id from the real table
)
As you insert into the content tables, also insert into the link table to create your globally unique id.
More simply, but even more manually, just hold a single value somewhere in the database. Whenever you need a new id, use that centrally stored value and increment it by one. Be sure to wrap the increment and collection in a single transaction to stop race conditions. (This can be done in a number of ways, depending on your flavor of SQL.)
EDIT
A couple of MySQL example lines of code from the web...
START TRANSACTION;
INSERT INTO foo (auto,text)
VALUES(NULL,'text'); # generate ID by inserting NULL
INSERT INTO foo2 (id,text)
VALUES(LAST_INSERT_ID(),'text'); # use ID in second table
COMMIT TRANSACTION;
Personally, I'd actually store the value in a variable, commit the transaction, and then continue with my business logic. This would keep the locks on the tables to a minimum.
You could have a separate ID table, insert into that, and use the newly-inserted ID.
e.g.
CREATE TABLE ids (INT UNSIGNED AUTO INCREMENT PRIMARY KEY, timeadded DATETIME);
In the script:
<?php
$r = mysql_query('INSERT INTO ids (timeadded) VALUES (NOW())');
$id = mysql_insert_id();
mysql_query("INSERT INTO someOtherTable (id, data) VALUES ('$id', '$data)");
Add error checking etc. to taste.
The MySQL manual states:
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.
(Source) So I don't think concerns about ACID complians are a problem.

splitting data into multiple tables

I am building a employees page.
Some of the information goes into an 'employees' table but some of it goes into a 'availability' table that is referenced to the 'employee' table:
availability:
id / employeeid (unique id from employees table) / monday available / and on and on /
So I don't have that unique ID from the employees table until I create them.
Is it fine to do a query where I set the employee info and then a query to get the last created row in the employee table and then use the unique id from that to set the availability...
Or is that messy and should I have a create employee page and THEN a set availability page?
So basically I want to know if it is cleaner and 'better' coding to separate the two functions?
Adding to #Quassnoi's answer:
You would add the employee record, then use the MySQL LAST_INSERT_ID() function to find the autoincremented unique id for the employee record you added. You can then feed that value back into the availability INSERT statement.
More details are on the MySQL manual page at http://dev.mysql.com/doc/refman/5.1/en/example-auto-increment.html
It's important that you not use a SELECT statement (e.g. SELECT MAX(employee.id)) since there might be other uses adding records as well. LAST_INSERT_ID() is specific to your connection
Of course create employee first, availability then.
If your tables are InnoDB, you can do it in a transaction so that you can rollback the whole update if something goes wrong.
Is it fine to do a query where I set
the employee info and then a query to
get the last created row in the
employee table and then use the unique
id from that to set the
availability...
Yes, that sounds OK. If you use an autoincrement column for employeeid, you can then use mysql_insert_id() or equivalent to retrieve that last inserted id safely. Don't do SELECT MAX(employeeid) FROM ...., because you might get problems when loads of people are using it concurrently.
You can easily get the last insered record via
mysql_insert_id()
After that, you can insert an availability record for the desired employee.
Note: I would choose a framework that takes care of these issues, like Symfony or Cake.
Using the "last created row" may not always work the way that you're expecting and may complicate things in the future if there's growth or if another programmer assumes the project. If I understand what you're looking for, you should instead have 3 tables. One table for employees, one table for availability, and a third table should be used to store unique records for the association. In the association table each row will have columns for : a unique ID, the employee id, the availability id.

how do i handle concurrent inserts in mysql table and fetch the correct insert id

I am using adodb for PHP library.
For fetching the id at which the record is inserted I use this function "$db->Insert_ID()"
I want to know if there are multiple and simultaneous inserts into the database table, will this method return me the correct inserted id for each record inserted ?
The reason I am asking this is because I use this last insert id for further processing of other records and making subsequent entries in the related tables.
Is this approach safe enough or am I missing something.
Please help me formulate a proper working plan for this so that I can use the last insert id for further inserts into the other table safely without having to mess up with the existing data.
Thanks
Yes, it's safe for concurent use. That's because LAST_INSERT_ID() is per-connection, as explained here:
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.
The $db->Insert_ID() will return you last insert id only so if you are inserting many records and want to get id of each last inserted row, then this will work successfully.
I want to know if there are multiple and simultaneous inserts into the database table, will this method return me the correct inserted id for each record inserted ?
It will return only the most recently inserted id.
In order to get ids for multiple inserts, you will have to call INSERT_ID() after each statement is executed. IE:
INSERT INTO TABLE ....
INSERT_ID()
INSERT INTO TABLE ....
INSERT_ID()
...to get the id value for each insert. If you ran:
INSERT INTO TABLE ....
INSERT INTO TABLE ....
INSERT_ID()
...will only return the id for the last insert statement.
Is this approach safe enough or am I missing something.
It's safer than using SELECT MAX(id) FROM TABLE, which risks returning a value inserted by someone else among other things relating to isolation levels.

Retrieving the index of an inserted row

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.

Categories