MySQL插入多个表?(数据库规范化?)


136

我尝试在insert同一查询中搜索多个表中信息的方式,但发现这是不可能的吗?所以我想要insert通过简单地使用多个查询来实现它,即;

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')

但是,如何为表赋予idusers到“手动” 的自动增量?useridprofile


8
您想了解交易。
2011年

3
可能重复的
有限赎罪

Answers:


241

不,您不能在一个MySQL命令中插入多个表。但是,您可以使用事务。

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;

看一下LAST_INSERT_ID()重用自动增量值。

编辑:您说:“ 经过所有这些时间试图弄清楚,它仍然无法正常工作。我不能简单地将刚刚生成的ID放在$ var中,然后将该$ var放入所有MySQL命令中吗?

让我详细说明:这里有3种可能的方式:

  1. 在上面的代码中。这在MySQL中完成了所有操作,LAST_INSERT_ID()第二条语句中的 in将自动成为在第一条语句中插入的autoincrement-column的值。

    不幸的是,当第二条语句本身在具有自动增量列的表中插入行时,LAST_INSERT_ID()它将被更新为表2的表,而不是表1的表。如果此后仍需要表1的表,我们将必须将其存储在一个变量中。这将我们引向方法2和3:

  2. LAST_INSERT_ID()在MySQL变量中存储:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
  3. 将以LAST_INSERT_ID()php变量(或您选择的任何可以连接到数据库的语言)存储:

    • INSERT ...
    • 使用您的语言来检索LAST_INSERT_ID(),方法是在MySQL中执行该文字语句,或者使用例如php mysql_insert_id()来为您完成的操作
    • INSERT [use your php variable here]

警告

无论选择哪种解决方案,都必须确定在两次查询之间执行被中断(例如,数据库服务器崩溃)时应该执行的操作。如果您可以接受“有些人已经完成,而另一些人还没有完成”,请不要继续阅读。

但是,如果您决定“要么所有查询都完成,要么都没有完成-我不希望某些表中的行,而另一些表中没有匹配的行,我总是希望我的数据库表是一致的”,则需要将所有语句包装在事务中。这就是为什么我在BEGINCOMMIT那里使用。

如果您需要更多信息,请再次评论:)


3
如果要插入两个以上的表,并且希望所有其他表具有唯一的ID和用户ID,该怎么办?那可能吗?
杰伊·威特

您可以将原始表中的last_insert_id放入MySQL变量中,并在所有其他表中使用该变量。如果您要一次处理很多表,则f00关于使用存储过程的建议更有意义。
Konerak 2011年

3
@Jay Wit:我更新了答案。“方法3”说明您确实可以将ID放入变量中,并在所有MySQL命令中重用它,但是如果希望数据库在崩溃时保持一致,则应该阅读有关事务的信息。
Konerak 2011年

3
当然,它们是可接受的MySQL语句,就像SELECT,UPDATE,INSERT和DELETE。首先编写一些测试脚本,如果一切正常,那您就很好了。
Konerak 2011年

2
@Konerak关于如何将这些多个SQL语句与已@mysql_variables准备好的语句结合使用的任何技巧?
费尔南多席尔瓦

17

如果使用存储过程,则非常简单:

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

完整脚本:

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;

2
抱歉,这是PHP吗?我从未见过这样的事情。我正在使用PHP和MySQL,有什么提示吗?
杰伊·威特

1
看起来像一长串sql语句
Melbourne2991

1
@JayWit:不,这些都是SQL语句。创建过程,然后您可以随时使用。参见dev.mysql.com/doc/refman/5.7/en/create-procedure.html
Pierre-Olivier Vares

7

如果要创建许多这样的记录(要注册10个用户,而不仅仅是一个用户),会发生什么情况?我找到以下解决方案(仅5个查询):

步骤I:创建临时表以存储新数据。

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

接下来,用值填充该表。

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

在这里,而不是 $ALL_VAL您放置值列表:('test1','test1','bio1','home1'),...,('testn','testn','bion','homen')

第二步:将数据发送到“用户”表。

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

如果您已经允许某些用户进入,则可以在此处使用“ IGNORE”。可选地,在此步骤之前,您可以使用类似于步骤III的UPDATE来查找已经在其中的用户(并将其标记在tmp表中)。在这里我们支持,用户名声明为PRIMARY在users表中。

第三步:应用更新以将所有用户ID从用户读取到tmp表。这是必不可少的步骤。

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

步骤IV:创建另一个表,使用用户的读取ID

INSERT INTO profiles (userid, bio, homepage) 
SELECT id, bio, homepage FROM tmp

1
它是6个查询-别忘了
拖放

3

试试这个

$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);
};

参考
PHP
MYSQL


2
如果服务器在用户创建之后但在配置文件创建之前崩溃,则很可能会导致某些不良行为。
2011年

@Vichle那么最好的方法是什么?
diEcho 2011年

2
@vichle将您的织补交易添加到此代码中,并已经停止了废话
您的常识

3
@Konerak我运行PHP脚本已有10多年了。他们俩都没有在行之间停下来的习惯。您最好考虑提高服务器质量,以使其不经常崩溃。这是网络,伙计。不是美联储。一次成功注册1亿个失败的用户,这没错。
您的常识

2
在此示例中,您可能是正确的。但是,我的代码始终用于记帐-无法忍受的想法是在A中删除钱,而不是在B中添加钱。此外,即使只是网络,交易也不那么困难/昂贵吗?恕我直言,他们养成良好的习惯。
Konerak 2011年

3

看看mysql_insert_id()

这里的文档:http : //in.php.net/manual/en/function.mysql-insert-id.php


1
该功能是数据库一致性的魔鬼。
2011年

同意-使用交易可能是更好的解决方案
Daniel Kutik

@vichle:是吗?我不经常使用php,但我认为这是他们呼吁LAST_INSERT_ID()程序员的捷径?
Konerak 2011年

1
所有查询均为交易。大多数编程语言中的默认设置是自动提交事务,因为大多数事务只是一个查询。mysql_insert_id()函数用于关闭自动提交的功能,但通常在错误的上下文中由不熟悉事务概念的人员使用。
2011年

3
@dnl您可以使用此功能并进行事务处理。此交易备注与问题无关。
您的常识

1

这是我为大学项目所做的方式,工作正常,可能不安全

$dbhost = 'localhost';
$dbuser = 'root';
$dbpass = '';
$conn = mysql_connect($dbhost, $dbuser, $dbpass);

$title =    $_POST['title'];            
$name =     $_POST['name'];         
$surname =  $_POST['surname'];                  
$email =    $_POST['email'];            
$pass =     $_POST['password'];     
$cpass =    $_POST['cpassword'];        

$check = 1;

if (){
}
else{
    $check = 1;
}   
if ($check == 1){

require_once('website_data_collecting/db.php');

$sel_user = "SELECT * FROM users WHERE user_email='$email'";
$run_user = mysqli_query($con, $sel_user);
$check_user = mysqli_num_rows($run_user);

if ($check_user > 0){
    echo    '<div style="margin: 0 0 10px 20px;">Email already exists!</br>
             <a href="recover.php">Recover Password</a></div>';
}
else{
    $users_tb = "INSERT INTO users ". 
           "(user_name, user_email, user_password) ". 
        "VALUES('$name','$email','$pass')";

    $users_info_tb = "INSERT INTO users_info".
           "(user_title, user_surname)".
        "VALUES('$title', '$surname')";

    mysql_select_db('dropbox');
    $run_users_tb = mysql_query( $users_tb, $conn );
    $run_users_info_tb = mysql_query( $users_info_tb, $conn );

    if(!$run_users_tb || !$run_users_info_tb){
        die('Could not enter data: ' . mysql_error());
    }
    else{
        echo "Entered data successfully\n";
    }

    mysql_close($conn);
}

}


-1

只是关于你说的一句话

嗨,我尝试过一种在同一查询的多个表中插入信息的方法

您是否在同一个碗中吃了所有午餐菜肴和饮料?
我想-不。

同样在这里。
有些事情我们分开做。
2个插入查询是2个插入查询。没关系。没错。无需将其捣碎。
选择相同。查询必须明智并能做到。那是唯一的原因。查询数量不是。

至于交易-您可以使用它们,但对于一般网站而言,这并不是什么大问题。毫无疑问,如果每年一次(如果有)一次用户注册被破坏,您将可以修复。
有成千上万个运行mysql且没有事务支持驱动程序的站点。您是否听说过可怕的灾难将这些地点分开?我也不。

mysql_insert_id()与事务有关。您可以将其纳入交易中。只是不同的事情。有人毫无疑问地提出了这个问题。


在容易避免的情况下,为什么还要冒险必须修理类似的东西?
2011年

@vichle我的表是myisam类型的。为了全文搜索,遗产和习惯。我冒着风险,是的。从理论上讲,可怕的危险正等着我。
您的常识

6
你为什么这么好斗?我只是说交易是去这里的方法。如果愿意,可以按自己的方式做,事实仍然是为这种情况创建了交易。
2011年

@vichle是的,我很苛刻,对不起。是您的That function is the devil of database consistency.,而不是交易。我认为交易没有什么不好。
您的常识

接受道歉。我的意思是,该功能经常被不了解交易存在的人滥用。这些人在php社区中也代表过多。
vichle 2011年

-4

对于PDO,您可以这样做

$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);

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

那正是我要的东西。
Subroto Biswas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.