MySQL Chèn vào nhiều bảng? (Chuẩn hóa cơ sở dữ liệu?)


136

Tôi đã thử tìm kiếm một cách để insertthông tin trong nhiều bảng trong cùng một truy vấn, nhưng phát hiện ra điều đó là không thể? Vì vậy, tôi muốn insertnó bằng cách sử dụng nhiều truy vấn tức là;

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

Nhưng làm thế nào tôi có thể tăng tự động idtừ users"hướng dẫn" useridcho profilebảng?


8
Bạn muốn tìm hiểu về các giao dịch.
vichle

Câu trả lời:


241

Không, bạn không thể chèn vào nhiều bảng trong một lệnh MySQL. Tuy nhiên, bạn có thể sử dụng các giao dịch.

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;

Có một cái nhìn LAST_INSERT_ID()để sử dụng lại các giá trị tự động.

Chỉnh sửa: bạn đã nói " Sau tất cả thời gian cố gắng tìm ra nó, nó vẫn không hoạt động. Tôi có thể đơn giản đặt ID vừa tạo vào $ var và đặt $ var đó trong tất cả các lệnh của MySQL không? "

Hãy để tôi giải thích: có 3 cách có thể ở đây:

  1. Trong mã bạn thấy ở trên. Điều này thực hiện tất cả trong MySQL và LAST_INSERT_ID()trong câu lệnh thứ hai sẽ tự động là giá trị của cột tự động được chèn trong câu lệnh đầu tiên.

    Thật không may, khi chính câu lệnh thứ hai chèn các hàng vào một bảng có cột tăng tự động, thì câu lệnh LAST_INSERT_ID()sẽ được cập nhật thành bảng 2 chứ không phải bảng 1. Nếu bạn vẫn cần bảng 1 sau đó, chúng ta sẽ phải lưu trữ nó trong một biến Điều này dẫn chúng ta đến cách 2 và 3:

  2. Sẽ lưu trữ LAST_INSERT_ID()trong một biến MySQL:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
  3. Sẽ lưu trữ LAST_INSERT_ID()một biến php (hoặc bất kỳ ngôn ngữ nào có thể kết nối với cơ sở dữ liệu mà bạn chọn):

    • INSERT ...
    • Sử dụng ngôn ngữ của bạn để truy xuất LAST_INSERT_ID(), bằng cách thực hiện câu lệnh bằng chữ đó trong MySQL hoặc sử dụng ví dụ php, mysql_insert_id()điều này giúp bạn
    • INSERT [use your php variable here]

CẢNH BÁO

Dù bạn chọn cách giải quyết nào, bạn phải quyết định điều gì sẽ xảy ra nếu việc thực thi bị gián đoạn giữa các truy vấn (ví dụ: máy chủ cơ sở dữ liệu của bạn gặp sự cố). Nếu bạn có thể sống với "một số đã kết thúc, một số khác thì không", đừng đọc tiếp.

Tuy nhiên, nếu bạn quyết định "tất cả các truy vấn kết thúc hoặc không kết thúc - tôi không muốn các hàng trong một số bảng nhưng không có các hàng khớp trong các bảng khác, tôi luôn muốn các bảng cơ sở dữ liệu của mình nhất quán", bạn cần phải bọc tất cả các câu lệnh trong một giao dịch. Đó là lý do tại sao tôi sử dụng BEGINCOMMITở đây.

Bình luận lại nếu bạn cần thêm thông tin :)


3
Và nếu tôi muốn chèn vào hơn 2 bảng và tôi muốn tất cả các bảng khác có id duy nhất và userid thì sao? Điều đó có thể không?
Jay Wit

Bạn có thể đặt last_insert_id từ bảng gốc vào biến MySQL và sử dụng biến đó trong tất cả các bảng khác của bạn. Đề xuất của f00 cho việc sử dụng Quy trình được lưu trữ thậm chí còn có ý nghĩa hơn nếu bạn sẽ thao túng nhiều bảng trong một lần.
Konerak

3
@Jay Wit: Tôi đã cập nhật câu trả lời. 'Cách 3' giải thích bạn thực sự có thể đặt ID vào một biến và sử dụng lại nó trong tất cả các lệnh của MySQL, nhưng bạn nên đọc về các giao dịch nếu bạn muốn db của mình nhất quán trong trường hợp xảy ra sự cố.
Konerak

3
Chắc chắn, chúng được chấp nhận các câu lệnh MySQL, giống như CHỌN, CẬP NHẬT, CHERTN và XÓA. Trước tiên hãy tạo một bản kiểm tra nhỏ và nếu mọi thứ hoạt động tốt, bạn sẽ ổn.
Konerak

2
@Konerak có mẹo nào về cách sử dụng nhiều câu lệnh SQL này cùng với các @mysql_variablescâu lệnh đã chuẩn bị không?
Fernando Silva

17

khá đơn giản nếu bạn sử dụng các thủ tục được lưu trữ:

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

kịch bản đầy đủ:

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
Xin lỗi, nhưng đây có phải là PHP không? Tôi chưa bao giờ thấy bất cứ điều gì như thế này. Tôi đang sử dụng PHP và MySQL, có mẹo gì không?
Jay Wit

1
trông giống như một tập hợp dài các câu lệnh sql
Melbourne2991

1
@JayWit: Không, đây là tất cả các câu lệnh SQL. Tạo các thủ tục, sau đó bạn có thể sử dụng bất cứ lúc nào bạn muốn. Xem dev.mysql.com/doc/refman/5.7/en/create-procedure.html
Pierre-Olivier Vares

7

Điều gì sẽ xảy ra, nếu bạn muốn tạo nhiều bản ghi như vậy (để đăng ký 10 người dùng, không chỉ một)? Tôi tìm giải pháp sau (chỉ 5 truy vấn):

Bước I: Tạo bảng tạm thời để lưu trữ dữ liệu mới.

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

Tiếp theo, điền vào bảng này với các giá trị.

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

Tại đây, thay vì $ALL_VALbạn đặt danh sách các giá trị: ('test1', 'test1', 'bio1', 'home1'), ..., ('testn', 'testn', 'bion', 'homen')

Bước II: Gửi dữ liệu đến bảng 'người dùng'.

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

Tại đây, "IGNORE" có thể được sử dụng, nếu bạn cho phép một số người dùng đã ở bên trong. Tùy chọn bạn có thể sử dụng CẬP NHẬT tương tự như bước III, trước bước này, để tìm người dùng đã ở bên trong (và đánh dấu họ trong bảng tmp). Ở đây chúng tôi hỗ trợ, tên người dùng đó được khai báo như PRIMARYtrong bảng người dùng.

Bước III: Áp dụng cập nhật để đọc tất cả id người dùng từ bảng người dùng vào bảng tmp. ĐÂY LÀ BƯỚC ESSENTIAL.

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

Bước IV: Tạo bảng khác, sử dụng id đọc cho người dùng

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

1
đó là 6 truy vấn - đừng quên DROP
TEMPORary

3

thử cái này

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

Tài liệu tham khảo
PHP
MYSQL


2
Điều này rất có thể sẽ dẫn đến một số hành vi không mong muốn nếu máy chủ gặp sự cố sau khi người dùng tạo nhưng trước khi tạo hồ sơ.
vichle

@Vichle thì cách nào là tốt nhất ??
diEcho

2
@vichle thêm giao dịch mờ nhạt của bạn vào mã này và dừng việc vô lý đó lại
Ý thức chung của bạn

3
@Konerak Tôi đang chạy các kịch bản PHP được hơn 10 năm. Không ai trong số họ có thói quen dừng lại giữa các dòng. bạn nên nghĩ đến việc cải thiện chất lượng máy chủ của mình để làm cho nó không bị lỗi thường xuyên. Đó là web, anh bạn. Đó không phải là Cục Dự trữ Liên bang. Không có gì sai với một đăng ký người dùng bị hỏng 100 000 000 người thành công.
Ý thức chung của bạn

2
Trong ví dụ này, bạn có thể đúng. Mã của tôi luôn được sử dụng cho kế toán mặc dù - ý nghĩ loại bỏ tiền trong A và không thêm tiền vào B trong khi cần, là không thể chấp nhận được. Bên cạnh đó, ngay cả khi chỉ là web, một giao dịch không quá khó / tốn kém? IMHO, họ tạo thói quen tốt.
Konerak

3

hãy xem mysql_insert_id ()

đây là tài liệu: http://in.php.net/manual/en/feft.mysql-insert-id.php


1
Chức năng đó là ma quỷ của tính nhất quán cơ sở dữ liệu.
vichle

đồng ý - sử dụng giao dịch có thể là giải pháp tốt hơn
Daniel Kutik

@vichle: có phải vậy không? Tôi không sử dụng php thường xuyên, nhưng tôi đoán đó là lối tắt của họ để gọi LAST_INSERT_ID()cho lập trình viên?
Konerak

1
Tất cả các truy vấn là giao dịch. Mặc định trong hầu hết các ngôn ngữ lập trình là tự động cam kết giao dịch, vì hầu hết các giao dịch chỉ là một truy vấn. Hàm mysql_insert_id () có nghĩa là khi bạn đã tắt tự động cam kết, nhưng thường được sử dụng trong ngữ cảnh sai bởi những người không quen với khái niệm giao dịch.
vichle

3
@dnl bạn có thể sử dụng chức năng này VÀ một giao dịch đều ổn. nhận xét giao dịch này không liên quan đến câu hỏi.
Ý thức chung của bạn

1

Đây là cách mà tôi đã làm cho một dự án uni, hoạt động tốt, thăm dò không an toàn

$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

Chỉ là một nhận xét về câu nói của bạn

Xin chào, tôi đã thử tìm cách chèn thông tin vào nhiều bảng trong cùng một truy vấn

Bạn có ăn tất cả các món ăn trưa trộn với đồ uống trong cùng một bát không?
Tôi cho rằng - không.

Tương tự ở đây.
Có những thứ chúng tôi làm riêng.
2 truy vấn chèn là 2 truy vấn chèn. Không sao đâu Không có gì sai với nó. Không cần phải nghiền nó trong một.
Tương tự cho chọn. Truy vấn phải hợp lý và thực hiện công việc của nó. Đó là lý do duy nhất. Số lượng truy vấn không.

Đối với các giao dịch - bạn có thể sử dụng chúng, nhưng đó không phải là vấn đề lớn đối với trang web trung bình. Nếu điều đó xảy ra mỗi năm một lần (nếu có) rằng một đăng ký người dùng bị hỏng, bạn có thể sửa chữa, không còn nghi ngờ gì nữa.
có hàng trăm ngàn trang web đang chạy mysql mà không có trình điều khiển hỗ trợ giao dịch. Bạn đã nghe nói về những thảm họa khủng khiếp phá vỡ các trang web này? Tôi cũng không.

Và mysql_insert_id () đã lưu ý đến các giao dịch. bạn có thể bao gồm trong giao dịch tất cả các quyền. nó chỉ là vấn đề khác nhau Ai đó đưa ra câu hỏi này từ hư không.


Tại sao bạn muốn mạo hiểm phải sửa những thứ như vậy khi dễ dàng tránh được?
vichle

@vichle bảng của tôi thuộc loại myisam. Vì lợi ích của tìm kiếm fulltext, di sản và thói quen. Tôi đang mạo hiểm, yup. Thật là một sự nguy hiểm khủng khiếp (về lý thuyết) đang chờ đợi tôi.
Ý thức chung của bạn

6
Tại sao bạn lại hung hăng như vậy? Tôi chỉ nói rằng các giao dịch là cách để đi đến đây. Bạn làm theo cách của bạn nếu bạn muốn, thực tế vẫn là các giao dịch được tạo ra cho loại tình huống này.
vichle

@vichle yup, tôi khá gay gắt, tôi xin lỗi. Đó là của bạn That function is the devil of database consistency.mà thực hiện điều đó, không phải giao dịch. Tôi thấy không có gì xấu trong giao dịch.
Ý thức chung của bạn

Lời xin lỗi được chấp nhận. Tất cả những gì tôi muốn nói là chức năng này thường bị lạm dụng bởi những người không nhận thức được sự tồn tại của các giao dịch. Những người này cũng được thể hiện khá nhiều trong cộng đồng php.
vichle

-4

Đối với PDO Bạn có thể làm điều này

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

Đó chính xác là những gì tôi đang tìm kiếm.
Subroto Biswas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.