154

I tried searching a way to insert information in multiple tables in the same query, but found out it's impossible? So I want to insert it by simply using multiple queries i.e;

INSERT INTO users (username, password) VALUES('test', 'test')
INSERT INTO profiles (userid, bio, homepage) VALUES('[id of the user here?]','Hello world!', 'http://www.stackoverflow.com')

But how can I give the auto-increment id from the users to the "manual" userid for the profile table?

AAEM
  • 1,837
  • 2
  • 18
  • 26
Jay Wit
  • 2,987
  • 7
  • 32
  • 34

7 Answers7

287

No, you can't insert into multiple tables in one MySQL command. You can however use transactions.

BEGIN;
INSERT INTO users (username, password)
  VALUES('test', 'test');
INSERT INTO profiles (userid, bio, homepage) 
  VALUES(LAST_INSERT_ID(),'Hello world!', 'http://www.stackoverflow.com');
COMMIT;

Have a look at LAST_INSERT_ID() to reuse autoincrement values.

You said "After all this time trying to figure it out, it still doesn't work. Can't I simply put the just generated ID in a $var and put that $var in all the MySQL commands?"

Let me elaborate: there are 3 possible ways here:

  1. In the code you see above. This does it all in MySQL, and the LAST_INSERT_ID() in the second statement will automatically be the value of the autoincrement-column that was inserted in the first statement.

    Unfortunately, when the second statement itself inserts rows in a table with an auto-increment column, the LAST_INSERT_ID() will be updated to that of table 2, and not table 1. If you still need that of table 1 afterwards, we will have to store it in a variable. This leads us to ways 2 and 3:

  2. Will stock the LAST_INSERT_ID() in a MySQL variable:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
    
  3. Will stock the LAST_INSERT_ID() in a php variable (or any language that can connect to a database, of your choice):

    • INSERT ...
    • Use your language to retrieve the LAST_INSERT_ID(), either by executing that literal statement in MySQL, or using for example php's mysql_insert_id() which does that for you
    • INSERT [use your php variable here]

WARNING

Whatever way of solving this you choose, you must decide what should happen should the execution be interrupted between queries (for example, your database-server crashes). If you can live with "some have finished, others not", don't read on.

If however, you decide "either all queries finish, or none finish - I do not want rows in some tables but no matching rows in others, I always want my database tables to be consistent", you need to wrap all statements in a transaction. That's why I used the BEGIN and COMMIT here.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Konerak
  • 39,272
  • 12
  • 98
  • 118
  • 3
    And what if I want to insert into more than 2 tables, and I want all the others to have a unique id and the userid? Is that possible? – Jay Wit Mar 03 '11 at 09:36
  • You could put the last_insert_id from the original table in a [MySQL variable](http://dev.mysql.com/doc/refman/5.0/en/select-into-statement.html), and use that variable in all your other tables. f00's suggestion for using a Stored Procedure makes even more sense if you're going to manipulate lots of table in one time. – Konerak Mar 03 '11 at 09:38
  • After all this time trying to figure it out, it still doesn't work. Can't I simply put the just generated ID in a $var and put that $var in all the MySQL commands? – Jay Wit Mar 04 '11 at 13:19
  • 4
    @Jay Wit: I updated the answer. 'Way 3' explains you indeed can put the ID in a variable and reuse it in all MySQL commands, but you should read about transactions if you want your db to be consistent in case of a crash. – Konerak Mar 04 '11 at 14:18
  • Thanks, worked like a charm! But can I use the BEGIN and COMMIT statement in PHP too? Thanks alot for helping me out! – Jay Wit Mar 04 '11 at 15:43
  • 3
    Sure, they are accepted MySQL statements, just like SELECT, UPDATE, INSERT and DELETE. Make a little testscript first, and if everything works fine, you're good to go. – Konerak Mar 04 '11 at 15:45
  • 2
    @Konerak any tips on how to use these multiple SQL statements with the `@mysql_variables` in conjunction with prepared statements? – Fernando Silva Apr 11 '14 at 22:41
  • i am 100th one nice answer and straight forward :) – Linus Dec 06 '15 at 17:10
  • `LAST_INSERT_ID()` might be the best thing I've ever seen. Learning about it solved the problem I was five seconds away from realizing I had. – Jtaylorapps Aug 02 '16 at 20:38
  • @Konerak but how can i store it in sqlstring ? – Smit Saraiya Oct 10 '16 at 09:21
  • @SmitSaraiya check the second example, "Will stock the LAST_INSERT_ID() in a MySQL variable". It's the `SELECT INTO` syntax. – Konerak Oct 10 '16 at 09:49
18

fairly simple if you use stored procedures:

call insert_user_and_profile('f00','http://www.f00.com');

full script:

drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null
)
engine=innodb;

drop table if exists user_profile;
create table user_profile
(
profile_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
homepage varchar(255) not null,
key (user_id)
)
engine=innodb;

drop procedure if exists insert_user_and_profile;

delimiter #

create procedure insert_user_and_profile
(
in p_username varchar(32),
in p_homepage varchar(255)
)
begin
declare v_user_id int unsigned default 0;

insert into users (username) values (p_username);
set v_user_id = last_insert_id(); -- save the newly created user_id

insert into user_profile (user_id, homepage) values (v_user_id, p_homepage);

end#

delimiter ;

call insert_user_and_profile('f00','http://www.f00.com');

select * from users;
select * from user_profile;
Jon Black
  • 16,223
  • 5
  • 43
  • 42
8

What would happen, if you want to create many such records ones (to register 10 users, not just one)? I find the following solution (just 5 queryes):

Step I: Create temporary table to store new data.

CREATE TEMPORARY TABLE tmp (id bigint(20) NOT NULL, ...)...;

Next, fill this table with values.

INSERT INTO tmp (username, password, bio, homepage) VALUES $ALL_VAL

Here, instead of $ALL_VAL you place list of values: ('test1','test1','bio1','home1'),...,('testn','testn','bion','homen')

Step II: Send data to 'user' table.

INSERT IGNORE INTO users (username, password)
SELECT username, password FROM tmp;

Here, "IGNORE" can be used, if you allow some users already to be inside. Optionaly you can use UPDATE similar to step III, before this step, to find whom users are already inside (and mark them in tmp table). Here we suppouse, that username is declared as PRIMARY in users table.

Step III: Apply update to read all users id from users to tmp table. THIS IS ESSENTIAL STEP.

UPDATE tmp JOIN users ON tmp.username=users.username SET tmp.id=users.id

Step IV: Create another table, useing read id for users

INSERT INTO profiles (userid, bio, homepage) 
SELECT id, bio, homepage FROM tmp
Unheilig
  • 16,196
  • 193
  • 68
  • 98
3

try this

$sql= " INSERT INTO users (username, password) VALUES('test', 'test') ";
mysql_query($sql);
$user_id= mysql_insert_id();
if(!empty($user_id) {

$sql=INSERT INTO profiles (userid, bio, homepage) VALUES($user_id,'Hello world!', 'http://www.stackoverflow.com');
/* or 
 $sql=INSERT INTO profiles (userid, bio, homepage) VALUES(LAST_INSERT_ID(),'Hello   world!', 'http://www.stackoverflow.com'); */
 mysql_query($sql);
};

References
PHP
MYSQL

Dharman
  • 30,962
  • 25
  • 85
  • 135
xkeshav
  • 53,360
  • 44
  • 177
  • 245
  • 3
    This would most likely result in some unwanted behaviour if the server crashes after the user creation but before the profile creation. – vichle Mar 03 '11 at 09:22
  • @Vichle then what is best way?? – xkeshav Mar 03 '11 at 09:25
  • Using transactions. Like Konerak did. If the server crashes before a transaction is finished, the changes made by the transaction will be rolled back. – vichle Mar 03 '11 at 09:28
  • don't listen to him. you can add a transaction to this code all right. but mysql_insert_id() itself has nothing to do with it. – Your Common Sense Mar 03 '11 at 09:39
  • Shrapnel, I'm not saying theres anything wrong with using mysql_insert_id, the function does what it does. It's just that it is most often used in a bad way. Like in the above example. It's meant for transactions. – vichle Mar 03 '11 at 09:41
  • @Col. Shrapnel: with all due respect, but even you must agree that in diEcho's code fragment, when the php-script stops between line 1 and 5, the database might be in an inconsistent state (insert 1 having executed, but insert 2 not) from an application point of view? – Konerak Mar 03 '11 at 09:53
  • 2
    In this example, you might be right. My code is always used for accounting though - the thought of removing money in A and not adding it in B while it should, is intolerable. Besides, even if it's just web, a transaction is not that hard/expensive? IMHO, they make good habits. – Konerak Mar 03 '11 at 10:03
  • @Konerak sure. in a form of suggestion - why not? But "this function" is not THAT "devil" as vichle pulls it. – Your Common Sense Mar 03 '11 at 10:06
3

have a look at mysql_insert_id()

here the documentation: http://in.php.net/manual/en/function.mysql-insert-id.php

Daniel Kutik
  • 6,997
  • 2
  • 27
  • 34
  • 1
    That function is the devil of database consistency. – vichle Mar 03 '11 at 09:24
  • agreed - using a transaction might be the better solution – Daniel Kutik Mar 03 '11 at 09:26
  • @vichle: is that so? I'm don't use php that often, but I figured it was their shortcut to calling `LAST_INSERT_ID()` for the programmer? – Konerak Mar 03 '11 at 09:27
  • 1
    All queries are transactions. Default in most programming languages is to auto commit transactions, since most transactions are just one query. The mysql_insert_id() function is meant for when you have turned off auto commit, but is very often used in the wrong context by people not familiar to the transaction concept. – vichle Mar 03 '11 at 09:34
  • 3
    @dnl you can use this function AND a transaction all right. this transaction remark is irrelevant to the question. – Your Common Sense Mar 03 '11 at 09:37
-2

Just a remark about your saying

Hi, I tried searching a way to insert information in multiple tables in the same query

Do you eat all your lunch dishes mixed with drinks in the same bowl?
I suppose - no.

Same here.
There are things we do separately.
2 insert queries are 2 insert queries. It's all right. Nothing wrong with it. No need to mash it in one.
Same for select. A query must be sensible and do its job. That's the only reasons. Number of queries is not.

There is no point in looking for a way to stuff different queries in one call. Different calls is how the database API is meant to work.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • Why would you want to risk having to fix stuff like that when it's easily avoidable? – vichle Mar 03 '11 at 09:55
  • @vichle my tables are of myisam type. For sake of fulltext search, legacy and habit. I am risking, yup. What a terrible (in theory) peril is waiting me. – Your Common Sense Mar 03 '11 at 10:07
  • 7
    Why are you being so aggressive? I'm just stating that transactions is the way to go here. You do it your way if you wish, the fact is still that transactions were created for this kind of situation. – vichle Mar 03 '11 at 10:16
  • @vichle yup, I was quite harsh, I apologize. It was yours `That function is the devil of database consistency.` which made that, not transactions. I see nothing bad in transactions. – Your Common Sense Mar 03 '11 at 10:23
  • Apology accepted. All I meant was that the function is often misused by people who are not aware of the existence of transactions. These people are also quite overrepresented in the php community. – vichle Mar 03 '11 at 10:45
  • @vichle but it's just an alias of LAST_INSERT_ID() which being used inside of a transaction anyway. I see no point in your war with this function – Your Common Sense Mar 03 '11 at 10:47
  • @YourCommonSense You say "_2 insert queries are 2 insert queries. It's all right. Nothing wrong with it. No need to mash it in one. **Same for select**._" This last one is pretty wrong statement... Multiple separate SELECTS might be **way more expensive** than a single SELECT with joins or subqueries... The difference might be **huge**!!! – dmikam Jul 08 '21 at 13:44
  • @dmikam I suppose that you, sadly, didn't get the actual context of this conversion. but yes, in your statement, which is unrelated to the question asked, you are correct. there is no point in running multiple queries where you can run one. It was nowhere asked though – Your Common Sense Jul 08 '21 at 15:31
  • @YourCommonSense Sure - it is out of context of the original question... But as you mentioned "_Same for select_" - selects are out of the context of the question too, so we are on the same wave :P – dmikam Jul 08 '21 at 15:43
  • 1
    @dmikam my answer is perfectly in the context of the question asked. You don't stuff a user authorization and a list of recent news in one select. You don't combine unrelated select queries EXACTLY like you don't combine two different inserts. – Your Common Sense Jul 08 '21 at 16:22
  • @YourCommonSense I think you just don't get the point of the original question. He is really inserting the same thing - user data... In fact all this data might be placed in the same table in many cases, but for some reason he desided to separate them (may be that he have multiple types of users some of which may have profile data and some not... or may be that his system permits having multiple profiles for the same user, but there is always a default one). Thera are multiple reasons and ways to combine insertion of this data. I'll just publish a separate answer. – dmikam Jul 09 '21 at 07:06
  • @dmikam I am answering web-dev related questions for more than 20 years. And I know what is the point. Which is just a vague idea that needs to be corrected. And no, this decade old question don't need any extra answers – Your Common Sense Jul 09 '21 at 07:14
-5

For PDO You may do this

$dbh->beginTransaction();

$stmt1 = "INSERT INTO users (username, password) VALUES('test', 'test')"; 
$stmt2 = "INSERT INTO profiles (userid, bio, homepage) VALUES('LAST_INSERT_ID(),'Hello world!', 'http://www.stackoverflow.com')";

$sth1 = $dbh->prepare($stmt1);
$sth2 = $dbh->prepare($stmt2);

$sth1->execute (array ('test','test'));
$sth2->execute (array ('Hello world!','http://www.stackoverflow.com'));

$dbh->commit();
Dharman
  • 30,962
  • 25
  • 85
  • 135
ocnet
  • 51
  • 5