แบบสอบถามเกี่ยวกับการรวมการปรับปรุงและการแทรกแบบสอบถามลงในแบบสอบถามเดียวใน mysql


9

ฉันต้องการติดตามประวัติของการเปลี่ยนแปลงสำหรับผู้ใช้ดังนั้นเมื่อใดก็ตามที่เขาเปลี่ยนโปรไฟล์ของฉันฉันจำเป็นต้องใช้ข้อมูลเก่าและเก็บไว้ในประวัติและอัปเดตด้วยข้อมูลใหม่

ฉันสามารถใช้selectเพื่อรับข้อมูลเก่าinsertประวัติและในที่สุดก็updateเปลี่ยนข้อมูล

ฉันสามารถมีทั้งหมดเหล่านี้ในแบบสอบถามเดียวใน mysql โดยไม่ต้องใช้วิธีการเก็บทริกเกอร์ ฯลฯ .. เช่นใช้ล็อค ฯลฯ ถ้าให้ฉันตัวอย่างเล็ก ๆ


1
@savaranan: คำถามนี้มีค่าเป็น +1 เนื่องจากจะมีการแจ้งเตือนที่ชัดเจนถึง DBAs และผู้พัฒนาเพื่อใช้ธุรกรรมและใช้ประโยชน์จากคุณสมบัติ ACID ของฐานข้อมูล
RolandoMySQLDBA

2
@savaranan: สำหรับเจตนาและวัตถุประสงค์ทั้งหมดแจ๊คได้ให้คำตอบที่เป็นไปได้เท่านั้นนั่นคือ ในความเป็นจริงแจ็คดักลาสใช้ขั้นตอนเพิ่มเติมและบังคับให้ล็อคเป็นระยะในทุกแถวที่มี id = 10 สำหรับการป้องกัน MVCC เพิ่มเติมโดยการเลือก SELECT ... สำหรับการอัพเดท คำตอบของเขาเน้นจุดแจ็คและฉันได้พูดตลอด: UPDATE และ INSERT ไม่สามารถและไม่เคยแบบสอบถามเดียวพวกเขาสามารถทำธุรกรรมเดียวสำหรับพฤติกรรม SQL ที่คำถามของคุณเสนอ
RolandoMySQLDBA

คำตอบ:


13

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

start transaction;
select id from t1 where id=10 for update;
insert into t2 select * from t1 where id=10;
update t1 set id = 11 where id=10;
commit;

สิ่งนี้ยอดเยี่ยมมากในการล็อคทุก ๆ แถวด้วย id = 10 นั่นควรเป็น +2 ทั้งหมดที่ฉันสามารถให้ได้คือ +1 !!!
RolandoMySQLDBA

1

ฉันไม่เชื่อว่าจะมีวิธีรวมทั้งสามข้อความ สิ่งที่ใกล้เคียงที่สุดที่ไม่ได้ช่วยคุณจริงๆและนั่นคือ SET SELECT ทางออกที่ดีที่สุดของคุณคือสิ่งกระตุ้น ด้านล่างเป็นตัวอย่างของทริกเกอร์ที่ฉันมักใช้เพื่อรักษาเพียงหลักฐานการตรวจสอบ (สร้างด้วย PHP):

$trigger = "-- audit trigger --\nDELIMITER $ \n".
    "DROP TRIGGER IF EXISTS `{$prefix}_Audit_Trigger`$\n".
    "CREATE TRIGGER `{$prefix}_Audit_Trigger` AFTER UPDATE ON `$this->_table_name` FOR EACH ROW BEGIN\n";

foreach ($field_defs as $field_name => $field) {
    if ($field_name != $id_name) {
       $trigger .= "IF (NOT OLD.$field_name <=> NEW.$field_name) THEN \n".'INSERT INTO AUDIT_LOG ('.
                    'Table_Name, Row_ID, Field_Name, Old_Value, New_Value, modified_by, DB_User) VALUES'.
                    "\n ('$this->_table_name',OLD.$this->_id_name,'$field_name',OLD.$field_name,NEW.$field_name,".
                    "NEW.modified_by, USER()); END IF;\n";
    }
}
$trigger .= 'END$'."\n".'DELIMITER ;';

-3

ฉันพบว่าแบบสอบถามนี้ใช้ได้กับเซิร์ฟเวอร์ SQL และ MySQL INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

หวังว่านี่จะเป็นประโยชน์กับคนอื่นในอนาคต


4
นี่ไม่ใช่แบบสอบถามจริงๆ นี่เป็นจริงสองแบบสอบถามซึ่งควรจะถือว่าเป็นธุรกรรม
RolandoMySQLDBA

@rolandomysqldba: มันทำงานได้ดีเป็นแบบสอบถามเดียวเมื่อฉันส่งไปยังเซิร์ฟเวอร์ db จากรหัสแอปพลิเคชันที่ฉันถือว่าชุดนี้เป็นแบบสอบถามเดียว ทำไมคุณพูดอย่างนั้น คุณสามารถหักล้างนี้กับเหตุผลที่ดี ..
Saravanan

2
@saravanan: ในสายตาของ InnoDB หรือ RDBMS ที่สอดคล้องกับ ACID (Oracle, SQLServer, PostreSQL, Sybase, ฯลฯ ) เป็นไปไม่ได้ที่จะเรียกใช้คำสั่ง SQL สองคำสั่งเดียว ในฐานะที่เป็นฐานข้อมูลที่เข้ากันได้กับกรดจะถือว่าเป็นสองคำสั่ง โดยค่าเริ่มต้น InnoDB ได้เปิดใช้งานการเติมข้อความอัตโนมัติ คำสั่งแรก INSERT จะดำเนินการเป็นธุรกรรมเดียว ข้อมูล Multiversioning Concurrency Control (MVCC) จะถูกสร้างขึ้นเพื่อเก็บสำเนาของข้อมูลดั้งเดิมในตาราง t2 บนพื้นฐานแบบแถวต่อแถว หาก MySQL ขัดข้องระหว่างการดำเนินการ INSERT InnoDB จะใช้ข้อมูล MVCC ในการย้อนกลับ t2 ไปเป็นสถานะดั้งเดิม
RolandoMySQLDBA

1
@saravanan: สมมติว่า INSERT ทำงานได้สำเร็จ ข้อมูลที่เป็นผลมาจาก INSERT ได้รับการยืนยันแล้ว (ด้วยการเปิดใช้งานอัตโนมัติ) และ MVCC ปกป้องตาราง t2 จะถูกยกเลิก เมื่อคุณทำการอัปเดต MVCC จะถูกสร้างขึ้นกับตาราง t1 และจะทำการอัปเดต หาก MySQL ขัดข้องในระหว่างการอัพเดท InnoDB ใช้ข้อมูล MVCC ใน t1 เพื่อย้อนกลับการอัปเดต แม้ว่า UPDATE จะเปลี่ยนไปเพียงหนึ่งแถว แต่ความเป็นไปได้แบบหนึ่งต่อหนึ่งมิลลิวินาทีมีอยู่ในการย้ายระเบียนจาก t1 เป็น t2 ด้วย id 10 และไม่เปลี่ยน id 10 เป็น id 11 ใน t1 เพื่อป้องกันสถานการณ์ที่ไม่ซ้ำกันนี้คุณต้องทำดังต่อไปนี้ ...
RolandoMySQLDBA

@savaranan: ถือว่าทั้งสองคำสั่ง SQL เป็นธุรกรรมเดียว วิธีง่ายๆในการทำเช่นนี้คือ: BEGIN; INSERT INTO t2 SELECT * จาก t1 WHERE id = 10; อัพเดต t1 SET id = 11 WHERE id = 10; COMMIT; เหตุผลที่แข็งแกร่งที่สุดสำหรับการดำเนินการกับคำสั่ง SQL สองรายการเนื่องจากธุรกรรมเดียวคือข้อเท็จจริงที่ว่า MVCC ที่สร้างขึ้นสำหรับ INSERT จะยังคงมีอยู่ในระหว่างการปรับปรุง หากเกิดความผิดพลาดของ MySQL ในระหว่างการอัพเดทภายในธุรกรรม (BEGIN; ... COMMIT; block) MVCC จะย้อนกลับการเปลี่ยนแปลงทั้งหมดในสถานะที่สอดคล้องกัน หากทั้ง INSERT และ UPDATE เสร็จสมบูรณ์ MVCC จะถูกยกเลิกในช่วงเวลาสุดท้าย
RolandoMySQLDBA
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.