160

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?

1
  • 8
    You want to learn about transactions. Commented Mar 3, 2011 at 9:16

7 Answers 7

292

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.

Sign up to request clarification or add additional context in comments.

11 Comments

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?
You could put the last_insert_id from the original table in a MySQL variable, 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.
@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.
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 any tips on how to use these multiple SQL statements with the @mysql_variables in conjunction with prepared statements?
|
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;

3 Comments

Sorry, but is this PHP? I've never seen anything like this. I'm using PHP and MySQL, any tips?
looks like a long set of sql statements
@JayWit : No, these are all SQL statements. Create the procedures, then you can use anytime you want. See dev.mysql.com/doc/refman/5.7/en/create-procedure.html
9

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

2 Comments

it is 6 queries - don't forget to DROP TEMPORARY
No need to drop Temporary - it will be dropped automatically when your session ends.
3

have a look at mysql_insert_id()

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

5 Comments

That function is the devil of database consistency.
agreed - using a transaction might be the better solution
@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?
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.
@dnl you can use this function AND a transaction all right. this transaction remark is irrelevant to the question.
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

8 Comments

This would most likely result in some unwanted behaviour if the server crashes after the user creation but before the profile creation.
@Vichle then what is best way??
Using transactions. Like Konerak did. If the server crashes before a transaction is finished, the changes made by the transaction will be rolled back.
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.
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.
|
-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.

12 Comments

Why would you want to risk having to fix stuff like that when it's easily avoidable?
@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.
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 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.
@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.
|
-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();

1 Comment

That is exactly what I am looking for.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.