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

แต่ฉันจะให้การเพิ่มอัตโนมัติidจากusersไปยัง "คู่มือ" useridสำหรับprofileตารางได้อย่างไร


8
คุณต้องการเรียนรู้เกี่ยวกับธุรกรรม
vichle

คำตอบ:


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()คำสั่งในคำสั่งที่สองจะเป็นค่าของ 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]

คำเตือน

ไม่ว่าคุณจะเลือกวิธีการแก้ปัญหาแบบใดคุณต้องตัดสินใจว่าจะเกิดอะไรขึ้นหากการดำเนินการถูกขัดจังหวะระหว่างการสืบค้น (ตัวอย่างเช่นเซิร์ฟเวอร์ฐานข้อมูลของคุณล่ม) หากคุณสามารถอยู่กับ "บางคนจบแล้วคนอื่นไม่ได้" อย่าอ่านต่อ

หากคุณตัดสินใจว่า "แบบสอบถามทั้งหมดเสร็จสิ้นหรือไม่เสร็จสิ้น - ฉันไม่ต้องการแถวในบางตาราง แต่ไม่มีแถวที่ตรงกันในผู้อื่นฉันต้องการให้ตารางฐานข้อมูลของฉันสอดคล้องกันเสมอ" คุณต้องตัดคำสั่งทั้งหมดในธุรกรรม นั่นเป็นเหตุผลที่ฉันใช้BEGINและCOMMITที่นี่

แสดงความคิดเห็นอีกครั้งหากคุณต้องการข้อมูลเพิ่มเติม :)


3
และถ้าฉันต้องการแทรกลงในตารางมากกว่า 2 ตารางและฉันต้องการให้คนอื่น ๆ ทั้งหมดมีรหัสเฉพาะและหมายเลขผู้ใช้ เป็นไปได้ไหม
Jay Wit

คุณสามารถวาง last_insert_id จากตารางต้นฉบับในตัวแปร MySQLและใช้ตัวแปรนั้นในตารางอื่น ๆ ของคุณทั้งหมด คำแนะนำของ f00 สำหรับการใช้ขั้นตอนการจัดเก็บทำให้เข้าใจได้ง่ายขึ้นถ้าคุณจะจัดการกับตารางจำนวนมากในคราวเดียว
Konerak

3
@Jay Wit: ฉันอัพเดตคำตอบแล้ว 'วิธีที่ 3' อธิบายว่าคุณสามารถใส่ ID ในตัวแปรและนำมาใช้ใหม่ในคำสั่ง MySQL ทั้งหมด แต่คุณควรอ่านเกี่ยวกับธุรกรรมถ้าคุณต้องการให้ db ของคุณสอดคล้องกันในกรณีที่เกิดความผิดพลาด
Konerak

3
แน่นอนว่าพวกเขาจะยอมรับคำสั่ง MySQL เช่น SELECT, UPDATE, INSERT และ DELETE ทำ testcript เล็ก ๆ น้อย ๆ ก่อนและถ้าทุกอย่างทำงานได้ดีคุณก็สบายดี
Konerak

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 มีคำแนะนำอะไรบ้าง?
Jay Wit

1
ดูเหมือนชุดคำสั่ง sql แบบยาว
Melbourne2991

1
@JayWit: ไม่นี่คือคำสั่ง SQL ทั้งหมด สร้างโพรซีเดอร์จากนั้นคุณสามารถใช้ได้ทุกเวลาที่คุณต้องการ ดูdev.mysql.com/doc/refman/5.7/en/create-procedure.html
Pierre-Olivier Vares

7

จะเกิดอะไรขึ้นถ้าคุณต้องการสร้างเรคคอร์ดดังกล่าวจำนวนมาก (เพื่อลงทะเบียนผู้ใช้ 10 คนไม่ใช่เพียงคนเดียว) ฉันค้นหาวิธีแก้ปัญหาต่อไปนี้ (เพียง 5 แบบสอบถาม):

ขั้นตอนที่ฉัน:สร้างตารางชั่วคราวเพื่อเก็บข้อมูลใหม่

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" ได้หากคุณอนุญาตให้ผู้ใช้บางคนอยู่ภายใน ไม่บังคับคุณสามารถใช้ UPDATE คล้ายกับขั้นตอนที่สามก่อนหน้าขั้นตอนนี้เพื่อค้นหาผู้ใช้ที่มีอยู่แล้วภายใน (และทำเครื่องหมายไว้ในตาราง tmp) ที่นี่เราระงับชื่อผู้ใช้นั้นจะถูกประกาศPRIMARYในตารางผู้ใช้

ขั้นตอนที่สาม:ใช้การอัปเดตเพื่ออ่าน id ผู้ใช้ทั้งหมดจากผู้ใช้กับตาราง tmp นี่คือขั้นตอนที่สำคัญ

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

ขั้นตอนที่สี่:สร้างตารางอื่นโดยใช้รหัสอ่านสำหรับผู้ใช้

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

1
มันคือ 6 ข้อความค้นหา - อย่าลืมที่จะลดเวลาชั่วคราว
Zigulik

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
นี่น่าจะส่งผลให้เกิดพฤติกรรมที่ไม่พึงประสงค์บางอย่างถ้าเซิร์ฟเวอร์ล้มเหลวหลังจากการสร้างผู้ใช้ แต่ก่อนที่จะสร้างโปรไฟล์
vichle

@Vichle แล้ววิธีที่ดีที่สุดคืออะไร ??
diEcho

2
@vichle เพิ่มธุรกรรม darn ของคุณในรหัสนี้และหยุดเรื่องไร้สาระนั้นแล้ว
Your Common Sense

3
@ Konerak ฉันใช้งานสคริปต์ PHP มานานกว่า 10 ปี ทั้งสองมีนิสัยหยุดระหว่างบรรทัด คุณควรคิดถึงการปรับปรุงคุณภาพเซิร์ฟเวอร์ของคุณให้ดีขึ้นเพื่อไม่ให้เกิดความผิดพลาดบ่อยครั้ง มันเป็นเว็บครับ มันไม่ใช่ Federal Reserve ไม่มีอะไรผิดปกติกับการลงทะเบียนผู้ใช้ที่เสียหายหนึ่งแสนคนที่ประสบความสำเร็จ
สามัญสำนึกของคุณ

2
ในตัวอย่างนี้คุณอาจพูดถูก รหัสของฉันใช้สำหรับการบัญชีเสมอ - ความคิดในการลบเงินใน A และไม่เพิ่มใน B ในขณะที่ควรจะเป็นมากเกินไป นอกจากนี้แม้ว่าจะเป็นเพียงแค่การทำธุรกรรมการทำธุรกรรมนั้นไม่ใช่เรื่องยาก / ราคาแพง? IMHO พวกเขาสร้างนิสัยที่ดี
Konerak

3

ลองดู mysql_insert_id ()

นี่คือเอกสาร: http://in.php.net/manual/en/function.mysql-insert-id.php


1
ฟังก์ชั่นนั้นคือปีศาจของความสอดคล้องของฐานข้อมูล
vichle

ตกลง - การใช้ธุรกรรมอาจเป็นทางออกที่ดีกว่า
Daniel Kutik

@ วิชี: เป็นอย่างนั้นเหรอ? ฉันไม่ได้ใช้ php บ่อย แต่ฉันคิดว่ามันเป็นทางลัดของพวกเขาที่จะเรียกร้องLAST_INSERT_ID()ให้โปรแกรมเมอร์หรือไม่
Konerak

1
แบบสอบถามทั้งหมดเป็นการทำธุรกรรม ค่าเริ่มต้นในภาษาการเขียนโปรแกรมส่วนใหญ่คือการยอมรับธุรกรรมโดยอัตโนมัติเนื่องจากธุรกรรมส่วนใหญ่เป็นเพียงแบบสอบถามเดียว ฟังก์ชั่น mysql_insert_id () มีไว้สำหรับเมื่อคุณปิดการส่งข้อมูลอัตโนมัติ แต่มักใช้ในบริบทที่ผิดโดยผู้ที่ไม่คุ้นเคยกับแนวคิดการทำธุรกรรม
vichle

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 () ไม่ได้ทำธุรกรรม คุณอาจรวมอยู่ในการทำธุรกรรมทั้งหมดที่ถูกต้อง มันเป็นเรื่องที่แตกต่าง มีคนยกคำถามนี้ออกจากที่ไหนเลย


ทำไมคุณต้องการเสี่ยงที่จะต้องแก้ไขสิ่งต่าง ๆ เช่นนั้นเมื่อมันสามารถหลีกเลี่ยงได้ง่าย?
vichle

@vichle ตารางของฉันเป็นประเภท myisam เพื่อประโยชน์ในการค้นหาแบบเต็มมรดกและนิสัย ฉันกำลังเสี่ยง สิ่งที่อันตราย (ในทางทฤษฎี) กำลังรอฉันอยู่
สามัญสำนึกของคุณ

6
ทำไมคุณถึงก้าวร้าว? ฉันแค่บอกว่าการทำธุรกรรมเป็นวิธีที่จะไปที่นี่ คุณทำในแบบของคุณถ้าคุณต้องการความจริงก็คือว่าการทำธุรกรรมถูกสร้างขึ้นสำหรับสถานการณ์เช่นนี้
vichle

@ วิชล์ yup ฉันค่อนข้างรุนแรงฉันขอโทษ มันเป็นของคุณThat function is the devil of database consistency.ที่ทำสิ่งนั้นไม่ใช่ธุรกรรม ฉันเห็นอะไรที่ไม่ดีในการทำธุรกรรม
สามัญสำนึกของคุณ

ขอโทษที่ยอมรับ สิ่งที่ฉันหมายถึงคือฟังก์ชั่นมักใช้ในทางที่ผิดโดยผู้ที่ไม่ได้ตระหนักถึงการมีอยู่ของการทำธุรกรรม คนเหล่านี้ก็มีอยู่มากในชุมชน php
vichle

-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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.