ในการปรับปรุงคีย์ซ้ำเหมือนกันกับการแทรก


158

ฉันค้นหาไปแล้วแต่ไม่พบว่าเป็นไปได้

ฉันแบบสอบถาม MySQL นี้:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

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

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

หรือ:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

ฉันได้ระบุทุกอย่างไว้ในส่วนเสริมแล้ว ...

หมายเหตุเพิ่มเติมฉันต้องการใช้งานเพื่อรับ ID!

id=LAST_INSERT_ID(id)

ฉันหวังว่าบางคนสามารถบอกฉันได้ว่าวิธีที่มีประสิทธิภาพที่สุดคืออะไร


9
AFAIK วิธีการคืนค่าแต่ละคอลัมน์a=VALUES(a), b=VALUES(b), ...เป็นวิธีที่คุณต้องไป นั่นเป็นวิธีที่ฉันทำเพื่อINSERT ON DUPLICATE KEY UPDATEงบของฉันทั้งหมด
Valdogg21

4
เพื่อชี้แจงทุกอย่างที่ระบุไว้ที่นี่ถูกต้องตราบใดที่คุณเข้าใจคำถามที่ถูกตอบคือ: คุณต้องเรียกคืนทุกคอลัมน์ที่คุณต้องการอัปเดตหรือไม่ ใช่ถ้าคุณต้องการแทรกหรืออัปเดต 8 คอลัมน์ในตาราง 25 คอลัมน์คุณต้องระบุ 8 คอลัมน์สองครั้ง - หนึ่งครั้งในส่วนแทรกและอีกครั้งในส่วนอัปเดต (คุณสามารถข้ามคีย์หลักในส่วนอัปเดตได้ แต่จะเป็นการง่ายที่สุดในการฟอร์แมตสตริง "a = 1, b = 2, c = 3" และฝังสองครั้ง) อย่างไรก็ตามคุณไม่จำเป็นต้องเรียกคืนคอลัมน์ที่เหลืออีก 17 คอลัมน์ คุณไม่ต้องการเปลี่ยน หากกลายเป็น UPDATE พวกเขาจะเก็บค่าที่มีอยู่
Timberline

3
ฉันเพิ่มความคิดเห็นนั้นเพราะคำถามและคำตอบทำให้ฉันไม่แน่ใจเกี่ยวกับคอลัมน์ที่ไม่ได้กล่าวถึงด้วย INSERT ชนิดนี้ซึ่งฉันทดสอบหลังจากเรียนรู้สิ่งที่จะทดสอบ INSERT ON DUPLICATE KEY UPDATE ออกจากคอลัมน์ที่ไม่ได้กล่าวถึงใน UPDATE แต่ให้ค่าเริ่มต้นใน INSERT ด้วย REPLACE INTO คอลัมน์ที่ไม่ได้กล่าวถึงจะได้รับค่าเริ่มต้นเสมอไม่ใช่ค่าที่มีอยู่
Timberline

1
ตรวจสอบstackoverflow.com/questions/2472229/…ฉันคิดว่ามีสิ่งที่คุณกำลังมองหา
Chococroc

คำตอบ:


82

UPDATEคำสั่งจะได้รับเพื่อให้สาขาเก่าสามารถปรับปรุงเพื่อให้ค่าใหม่ หากค่าเก่าของคุณเหมือนกันกับค่าใหม่ของคุณทำไมคุณต้องอัปเดตค่าในกรณีใด ๆ

สำหรับเช่น ถ้าคอลัมน์ของคุณaจะgถูกตั้งค่าไว้เป็น2ไป8; ไม่จำเป็นต้องอัปเดตอีกครั้ง

หรือคุณสามารถใช้:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

ที่จะได้รับidจากLAST_INSERT_ID; คุณต้องระบุแอปพลิเคชันส่วนหลังที่คุณใช้ด้วย

สำหรับ LuaSQL จะconn:getlastautoid()ดึงค่ามาใช้


6
มันเป็นเพียงตัวอย่าง ในแบบสอบถามชีวิตจริงมีความแตกต่าง คำถามของฉันเงียบง่าย ๆ : ฉันจำเป็นต้องระบุฟิลด์ทั้งหมด (และค่าในตัวอย่างแรกของฉัน) จริง ๆ หรือไม่ถ้าพวกเขาเหมือนกันในส่วนแทรก? ฉันต้องการแทรกทั้งหมดหรือหากมีการจับคู่ค่าที่ไม่ซ้ำกัน: อัปเดตทั้งหมด
Roy

1
"หมายเหตุเพิ่มเติมฉันต้องการใช้วิธีแก้ไขเพื่อรับรหัสด้วย!"
Agamemnus

1
@Tresdin เท่าที่ฉันสามารถบอกได้ว่ามันเป็นเพียงน้ำตาลซินแทคติค โดยส่วนตัวฉันไม่เคยเห็นใครใช้ a = VALUES (a), b = VALUES (b) ฯลฯ บางทีคุณอาจกำหนดค่าหลายค่าให้กับคอลัมน์ได้หรือไม่ ไม่แน่ใจว่าทำไมคุณไม่เชื่อมข้อมูลให้ตรงกันก่อน
DuckPuncher

54
ถ้าตารางtblมีอยู่แล้วแถว (1,10) จากนั้นINSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)จะตั้ง = 2 ในขณะที่INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=aจะตั้งค่า = 10
BệiViệtThành

2
ทำไม upvotes ทั้งหมด? สิ่งนี้ไม่ทำงาน ในฐานะที่เป็น @ BùiViệtThànhชี้ให้เห็นโดยใช้a=aการปรับปรุงอะไร (ข้อความค้นหาในคำถามเดิมไม่ได้อัปเดตสิ่งใดเลย แต่การอัปเดตน่าจะเป็นสิ่งที่ OP ต้องการ)
Roger Dueck

41

มีนามสกุลเฉพาะ MySQL กับ SQL ที่อาจเป็นสิ่งที่คุณต้องการ - REPLACE INTO

อย่างไรก็ตามมันไม่ทำงานเหมือนกันกับ 'ON DUPLICATE UPDATE'

  1. มันจะลบแถวเก่าที่ปะทะกับแถวใหม่แล้วแทรกแถวใหม่ ตราบใดที่คุณไม่มีคีย์หลักในตารางที่จะใช้ได้ แต่ถ้าคุณทำแล้วถ้าตารางอื่นอ้างอิงถึงคีย์หลักนั้น

  2. คุณไม่สามารถอ้างอิงค่าในแถวเก่าดังนั้นคุณจึงไม่สามารถเทียบเคียงได้

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

ฉันต้องการใช้งานเพื่อรับ ID!

สิ่งนี้ควรใช้งานได้ - last_insert_id () ควรมีค่าที่ถูกต้องตราบใดที่คีย์หลักของคุณกำลังเพิ่มขึ้นอัตโนมัติ

อย่างไรก็ตามอย่างที่ฉันพูดถ้าคุณใช้คีย์หลักนั้นในตารางอื่นREPLACE INTOอาจจะไม่เป็นที่ยอมรับของคุณเพราะมันจะลบแถวเก่าที่ปะทะกันผ่านคีย์เฉพาะ

มีคนแนะนำไว้ก่อนที่คุณจะลดการพิมพ์ได้โดยทำดังนี้

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);

ดังนั้นฉันไม่สามารถยึดคีย์หลักก่อนการreplace intoสืบค้นได้หรือไม่
Roy

ไม่ - แถวนั้นจะถูกลบและสร้างแถวใหม่ซึ่งจะมีคีย์หลักใหม่
Danack

สิ่งนี้จะทำให้กระบวนการช้าลงในชุดข้อมูลขนาดใหญ่และชอบการนำเข้า csv ด้วย 100K หรือมากกว่าแถว?
Muhammad Omer Aslam

24

ไม่มีวิธีอื่นฉันต้องระบุทุกอย่างสองครั้ง ที่หนึ่งสำหรับการแทรกที่สองในกรณีที่การปรับปรุง


9
สิ่งนี้ไม่ถูกต้องและไม่ควรเป็นคำตอบที่ยอมรับได้ คำตอบจาก @Danack คือคำตอบที่ถูกต้อง
Wayne Workman

2
คุณควรอ่านคำถาม @WayneWorkman อีกครั้งนี่คือคำตอบที่ถูกต้อง
Roy

24

นี่คือวิธีแก้ไขปัญหาของคุณ:

ฉันพยายามที่จะแก้ปัญหาเช่นคุณ & ฉันอยากจะแนะนำให้ทดสอบจากด้านง่าย

ทำตามขั้นตอนเหล่านี้: เรียนรู้จากวิธีแก้ปัญหาง่ายๆ

ขั้นตอนที่ 1: สร้างสคีมาของตารางโดยใช้ SQL Query นี้:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

ตาราง <code> ผู้ใช้ </code> พร้อมข้อมูล

ขั้นตอนที่ 2: สร้างดัชนีสองคอลัมน์เพื่อป้องกันข้อมูลที่ซ้ำกันโดยใช้ SQL Query ต่อไปนี้:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

หรือสร้างดัชนีของสองคอลัมน์จาก GUIดังนี้: สร้างดัชนีจาก GUI เลือกคอลัมน์เพื่อสร้างดัชนี ดัชนีของตาราง <code> ผู้ใช้ </code>

ขั้นตอนที่ 3: อัปเดตหากมีอยู่แทรกถ้าไม่ได้ใช้คิวรีต่อไปนี้:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

ตาราง <code> ผู้ใช้ </code> หลังจากเรียกใช้แบบสอบถามด้านบน


1
ขอบคุณสำหรับคำแนะนำนี้ - ทำให้ฉันเข้าใจได้รับการชื่นชมอย่างมากขอบคุณ!
Michael Nilsson

FYI การจัดเก็บรหัสผ่านเป็นข้อความล้วนเป็นความคิดที่แย่มากเนื่องจากพยายามป้องกันรหัสผ่านซ้ำในระดับฐานข้อมูล
OrangeDog

10

ในกรณีที่คุณมีความสามารถในการใช้ภาษาสคริปต์เพื่อเตรียมความพร้อมคำสั่ง SQL ของคุณคุณสามารถนำมาใช้ใหม่ฟิลด์ = ค่าคู่โดยใช้แทนSET(a,b,c) VALUES(a,b,c)

ตัวอย่างที่มี PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

ตารางตัวอย่าง:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

4
คุณควรหลีกเลี่ยงค่าของคุณหรือดำเนินการคำสั่งที่เตรียมไว้พร้อมกับค่าของคุณ
Chinoto Vokro

1
@ChinotoVokro - ฉันเห็นด้วย นี่เป็นเพียงการนำเสนอ "วิธีที่คุณสามารถทำได้" และไม่ได้ใช้ฟังก์ชันการสืบค้นใด ๆ ในตัวอย่าง
Chris

1
นี่เป็นทางเลือกที่ดีในการระบุอาเรย์ของคีย์และอาเรย์ของค่า ขอบคุณ
Mark Carpenter Jr

5

คุณอาจต้องการพิจารณาการใช้REPLACE INTOไวยากรณ์ แต่ได้รับการเตือนเมื่อคีย์หลัก / UNIQUE ซ้ำกันมันจะลบแถวและแทรกใหม่

คุณไม่จำเป็นต้องระบุฟิลด์ทั้งหมดอีกครั้ง อย่างไรก็ตามคุณควรพิจารณาถึงการลดประสิทธิภาพที่เป็นไปได้ (ขึ้นอยู่กับการออกแบบตารางของคุณ)

คำเตือน:

  • หากคุณมีคีย์หลัก AUTO_INCREMENT มันจะได้รับคีย์ใหม่
  • ดัชนีอาจจำเป็นต้องได้รับการอัพเดต

มีการละเมิดข้อ จำกัด หากดัชนีเป็นคีย์ต่างประเทศสำหรับตารางอื่นมันถูกทริกเกอร์โดยการลบส่วน
user3290180

4

ฉันรู้ว่ามันสาย แต่ฉันหวังว่าจะมีคนช่วยคำตอบนี้

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

คุณสามารถอ่านบทช่วยสอนด้านล่างได้ที่นี่:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/


คำตอบที่ดี. แต่การใช้ VALUES () เพื่อดูที่แถวใหม่จะถูกเลิกใช้เมื่อ MySQL 8.0.20 วิธีการใหม่ที่มีรายละเอียดที่ลิงค์คือการใช้นามแฝงสำหรับแถว ในตัวอย่างนี้นามแฝงคือnew:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473

@ user697473 ฉันได้รับข้อผิดพลาด: "คุณมีข้อผิดพลาดในไวยากรณ์ SQL ของคุณตรวจสอบคู่มือที่สอดคล้องกับรุ่นเซิร์ฟเวอร์ MariaDB ของคุณเพื่อหาไวยากรณ์ที่ถูกต้องเพื่อใช้ใกล้กับ 'AS new ON DUPLICATE KEY UPDATE a = new.a'" และการสืบค้นของฉันใช้งานได้ (ยกเว้นเมื่อมีการทำกุญแจซ้ำกันอย่างเห็นได้ชัด) ก่อนที่ฉันจะดำเนินการตามข้อนี้ ความคิดใด ๆ
รอน

@aaron - ขอโทษไม่มีความคิดที่ดี MySQL 8.0.20 เพิ่งเปิดตัวเมื่อปลายเดือนเมษายน บางที MariaDB ยังไม่ทัน นั่นอาจอธิบายได้ว่าทำไมมันถึงทำให้คุณมีข้อผิดพลาด
user697473

@ user697473 ใช่ฉันแค่ลอง a = VALUES (a) method ในคำตอบดั้งเดิมและมันก็ใช้ได้ขอบคุณอีกครั้ง
รอน

-7

คุณสามารถใช้การเพิกเฉยต่อการแทรกสำหรับกรณีนี้มันจะเพิกเฉยหากได้รับการบันทึกซ้ำ INSERT IGNORE ... ; - ไม่ใช้คีย์ซ้ำซ้อน


ฉันต้องการที่จะแทรกมันเมื่อไม่ได้อยู่ที่อื่นปรับปรุงมัน ไม่สนใจเลย
Roy

ทำไมคุณต้องการอัปเดตหากคุณอัปเดตด้วยค่าก่อนหน้า / เหมือนกันหากเป็นค่าใหม่ก็ถือว่าใช้ได้ถ้าไม่ใส่การเพิกเฉยต่อการทำงาน
pitu

2
เป็นไปได้มากเนื่องจากค่าอาจเปลี่ยนแปลงและเขาต้องการสร้างเร็กคอร์ดหากไม่มีอยู่ แต่อัพเดตเร็กคอร์ดที่มีอยู่ถ้ามัน (พร้อมการเปลี่ยนแปลง) โดยไม่มีค่าใช้จ่ายของการเดินทางไปกลับไปยังแอปพลิเคชันแล้วกลับไปยังฐานข้อมูล อีกครั้ง
mopsyd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.