ทำซ้ำ / คัดลอกระเบียนในตาราง MySQL เดียวกัน


87

ฉันมองหามาระยะหนึ่งแล้ว แต่ฉันไม่สามารถหาวิธีง่ายๆสำหรับปัญหาของฉันได้ ฉันต้องการทำซ้ำระเบียนในตาราง แต่แน่นอนว่าต้องอัปเดตคีย์หลักที่ไม่ซ้ำกัน

ฉันมีคำถามนี้:

INSERT INTO invoices
    SELECT * FROM invoices AS iv WHERE iv.ID=XXXXX
    ON DUPLICATE KEY UPDATE ID = (SELECT MAX(ID)+1 FROM invoices)

ปัญหาคือสิ่งนี้เพียงแค่เปลี่ยนIDแถวแทนที่จะคัดลอกแถว ไม่มีใครรู้วิธีแก้ไขปัญหานี้?

// แก้ไข: ฉันต้องการดำเนินการนี้โดยไม่ต้องพิมพ์ชื่อเขตข้อมูลทั้งหมดเนื่องจากชื่อเขตข้อมูลสามารถเปลี่ยนแปลงได้ตลอดเวลา

คำตอบ:


137

วิธีที่ฉันมักจะพูดถึงคือการใช้โต๊ะชั่วคราว มันอาจจะไม่มีประสิทธิภาพในการคำนวณ แต่ดูเหมือนว่าจะใช้ได้! ฉันกำลังทำซ้ำบันทึก 99 อย่างครบถ้วนสร้างบันทึก 100

CREATE TEMPORARY TABLE tmp SELECT * FROM invoices WHERE id = 99;

UPDATE tmp SET id=100 WHERE id = 99;

INSERT INTO invoices SELECT * FROM tmp WHERE id = 100;

หวังว่าจะได้ผลสำหรับคุณ!


ฉันชอบมันขั้นตอนเดียวที่จะจับตาดูสิ่งต่างๆ
SeanDowney

4
หากคุณกำลังคัดลอกระเบียนเดียวคุณสามารถวางตำแหน่งในการอัปเดตและแทรกได้ จากนั้นคุณสามารถกดขึ้นสองครั้งในคอนโซล mysql ซึ่งจะแสดงการดำเนินการถัดไปในประวัติเปลี่ยน id ในการอัปเดตซึ่งสะดวกในตอนท้ายกด Enter กดขึ้นกด Enter อีกครั้งโดยไม่ต้องเปลี่ยนแปลงอะไรแล้วทำซ้ำ ขั้นตอนสำหรับสำเนาหลายชุด เร็วขึ้นมาก
Cristian Vrabie

4
จะเป็นอย่างไรเมื่อมี id 100 อยู่แล้ว Secondlty ถ้าคุณเลือก id ใหม่สำหรับแถวใหม่ของคุณและในระหว่างการดำเนินการมีคนอื่นแทรกแถวใหม่จากนั้น id ของคุณและ id ที่เพิ่งใส่จะเหมือนกัน นี่คือคอขวดในรหัสนี้มี
nbhatti2001

6
คุณสามารถใช้ NULL:CREATE TEMPORARY TABLE tmp SELECT bla FROM invoices WHERE bla; UPDATE tmp SET id=NULL; INSERT INTO invoices SELECT * FROM tmp;
Tobias Beuving

1
@TobiasBeuving นั่นคือข้อผิดพลาดการคัดลอกวางเพราะผมได้ตั้งอยู่บนNOT NULL idฉันแค่สงสัยว่าคุณมีคำแนะนำสำหรับกรณีนี้หรือไม่ ถือเป็นการปฏิบัติที่ไม่ดีหรือไม่?
Marcel

84

คำตอบของ Alex ต้องการการดูแลบางอย่าง (เช่นการล็อกหรือการทำธุรกรรม) ในสภาพแวดล้อมแบบหลายไคลเอ็นต์

สมมติว่าAUTO IDฟิลด์เป็นฟิลด์แรกในตาราง (กรณีปกติ) เราสามารถใช้ประโยชน์จากธุรกรรมโดยปริยายได้

    สร้างตารางชั่วคราว tmp SELECT * จากใบแจ้งหนี้ WHERE ... ;
    แก้ไขรหัสดร็อป tmp ของตาราง; # ดรอปฟิลด์การเพิ่มอัตโนมัติ
    # อัพเดท tmp SET ... ; # เพียงแค่เปลี่ยนคีย์เฉพาะอื่น ๆ
    ใส่ใบแจ้งหนี้ SELECT 0, tmp. * จาก tmp;
    วางตาราง tmp;

จากเอกสาร MySQL:

การใช้ AUTO_INCREMENT: คุณยังสามารถกำหนดค่า NULL หรือ 0 ให้กับคอลัมน์อย่างชัดเจนเพื่อสร้างหมายเลขลำดับ


สิ่งนี้ทำให้บันทึกด้วย ID เป็น 0 สำหรับฉันแม้ว่าฉันจะมีฟิลด์ AUTO_INCREMENT การเปลี่ยนเป็น null ใช้งานได้ดังนั้นฉันขอแนะนำให้คุณเปลี่ยนรหัสเพื่อใช้ null แทน 0 ฉันใช้ MySQL 5.5.35 (Ubuntu)
Tyler Collier

@TylerCollier: NULLหรือ0ทั้งสองอย่างเป็นที่ยอมรับ คุณสามารถโพสต์ภาพหน้าจอของปัญหาโดยสร้างซ้ำและแบ่งปันได้ที่นี่
Ravinder Reddy

สมองของฉันรู้สึกเจ็บเมื่อพยายามทำความเข้าใจคำถามย่อยของ neato ในบรรทัดที่ 4 บางทีคำอธิบายนี้อาจช่วยได้: ตารางปลายทางinvoicesมีIDฟิลด์AUTO_INCREMENT ตารางต้นทางtmpไม่ เพื่อให้ได้ความสัมพันธ์แบบ 1: 1 ที่ต้องการระหว่างการแทรกคุณต้องระบุ a 0เป็นฟิลด์แรกของการเลือกซึ่งเป็นฟิลด์ AUTO_INCREMENT ของตารางปลายทาง เลือกทุกเขตข้อมูลจากตารางแหล่งที่มาtmp.* tmpสมบูรณ์แบบ! ตอนนี้คุณมีสหสัมพันธ์ 1: 1 แล้ว คำตอบที่ยอดเยี่ยม @Tim
ข้อศอกลอบสเตอร์คอกม้า

@RavinderReddy NULL และ 0 ไม่เหมือนกัน - เป็นไปได้ที่จะมี record-id เป็น 0 - ใช้ NULL สำหรับสิ่งนี้
Tobias Beuving

@TobiasBeuving : ฉันรู้ดีNULLและ0ไม่เหมือนกัน บริบทปัจจุบันอยู่ในAUTO_INCREMENTคุณลักษณะ คุณไม่สามารถแทรก a 0หรือNULLค่าลงในช่องดังกล่าว แต่เมื่อคุณใช้พวกเขาเป็นปัจจัยการผลิตลงในช่องดังกล่าวค่าลำดับถัดไปจะได้รับมอบหมายให้เขตข้อมูลที่อัตโนมัติ matically และด้วยเหตุนี้ความคิดเห็นของฉัน หลักฐานเกี่ยวกับ SQL Fiddle: sqlfiddle.com/#!9/d619f/1
Ravinder Reddy

12

คุณรู้แน่นอนว่า DUPLICATE KEY จะทริกเกอร์ดังนั้นคุณสามารถเลือก MAX (ID) +1 ล่วงหน้า:

INSERT INTO invoices SELECT MAX(ID)+1, ... other fields ... FROM invoices AS iv WHERE iv.ID=XXXXX 

2
ใช่ แต่ฉันไม่ต้องการพิมพ์ช่องอื่น ๆ ทั้งหมด (มีมากกว่าหรือน้อยกว่า 20 ช่องและอาจมีการเปลี่ยนแปลงเมื่อเวลาผ่านไป) ฉันไม่ต้องการเปลี่ยนรหัสทุกครั้งที่ตารางมีการเปลี่ยนแปลง
ตัวเลข

คุณจะต้อง คุณควรสร้างสคริปต์ที่สร้างคำสั่ง SQL จากรายการเขตข้อมูล มันเป็นเรื่องเล็กน้อย
Ingo

ทางเลือกเดียวคือการใช้ทริกเกอร์แทรก (หาก mysql รองรับ)
Ingo

11

แนวทางของคุณดี แต่ปัญหาคือคุณใช้ "*" แทนการกำหนดชื่อฟิลด์ หากคุณใส่ชื่อคอลัมน์ทั้งหมด excep คีย์หลักสคริปต์ของคุณจะทำงานเหมือน charm ในหนึ่งหรือหลายระเบียน

INSERT INTO invoices (iv.field_name, iv.field_name,iv.field_name ) SELECT iv.field_name, iv.field_name,iv.field_name FROM invoices AS iv WHERE iv.ID=XXXXX


อย่างไรก็ตามนั่นหมายความว่าคุณจะต้องเปลี่ยนรหัสทันทีที่มีการเพิ่มหรือลดฟิลด์จากตารางซึ่งโดยส่วนตัวแล้วฉันพบว่า MAJOR no-no (ไม่เสมอไป แต่ก็เพียงพอแล้ว)
Roemer

10

ฉันรู้คำตอบช้า แต่ก็ยังคงเป็นคำถามที่พบบ่อยฉันต้องการเพิ่มคำตอบอื่นที่ได้ผลสำหรับฉันโดยใช้insert intoคำสั่งบรรทัดเดียวเท่านั้นและฉันคิดว่ามันตรงไปตรงมาโดยไม่ต้องสร้างตารางใหม่ (เนื่องจากทำได้ เป็นปัญหาเกี่ยวกับCREATE TEMPORARY TABLEสิทธิ์):

INSERT INTO invoices (col_1, col_2, col_3, ... etc)
  SELECT
    t.col_1,
    t.col_2,
    t.col_3,
    ...
    t.updated_date,
  FROM invoices t;

โซลูชันนี้ใช้งานได้กับAUTO_INCREMENTคอลัมน์ id มิฉะนั้นคุณสามารถเพิ่มIDคอลัมน์ได้เช่นกันในคำสั่ง:

INSERT INTO invoices (ID, col_1, col_2, col_3, ... etc)
  SELECT
    MAX(ID)+1,
    t.col_1,
    t.col_2,
    t.col_3,
    ... etc ,
  FROM invoices t;

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


2
นี่เป็นพื้นฐานที่ดีสำหรับการแก้ไขบางฟิลด์ในขณะที่คัดลอก ฉันเพิ่งใช้INSERT into wp_options SELECT NULL, 'theme_mods_twopress', option_value, autoload FROM wp_options where option_name = 'theme_mods_onepress';คัดลอกในขณะที่เพิ่มรายการ ( AUTO_INCREMENT NULLเคล็ดลับจากด้านบน) และoption_nameฟิลด์ด้วย
Marcel Waldvogel

ครับ .. Good Idea for me. ขอขอบคุณ!!
Ragu Natarajan

ทางออกที่ดีที่สุด
Erlon Charles

3

ฉันแค่อยากจะขยายคำตอบที่ยอดเยี่ยมของ Alex เพื่อให้เหมาะสมหากคุณต้องการทำซ้ำชุดระเบียนทั้งหมด:

SET @x=7;
CREATE TEMPORARY TABLE tmp SELECT * FROM invoices;
UPDATE tmp SET id=id+@x;
INSERT INTO invoices SELECT * FROM tmp;

ฉันต้องทำสิ่งนี้และพบว่าคำตอบของ Alex เป็นจุดกระโดดที่สมบูรณ์แบบ! แน่นอนคุณต้องตั้งค่า @x เป็นหมายเลขแถวสูงสุดในตาราง (ฉันแน่ใจว่าคุณสามารถคว้ามันได้ด้วยแบบสอบถาม) สิ่งนี้มีประโยชน์ในสถานการณ์ที่เฉพาะเจาะจงเท่านั้นดังนั้นโปรดใช้ความระมัดระวังเมื่อคุณไม่ต้องการทำซ้ำทุกแถว ปรับคณิตศาสตร์ตามความจำเป็น


2

ฉันมีปัญหาที่คล้ายกันและนี่คือสิ่งที่ฉันกำลังทำ:

insert into Preguntas  (`EncuestaID`, `Tipo` , `Seccion` , `RespuestaID` , `Texto` )  select '23', `Tipo`, `Seccion`, `RespuestaID`, `Texto` from Preguntas where `EncuestaID`= 18

รับ Preguntas:

CREATE TABLE IF NOT EXISTS `Preguntas` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `EncuestaID` int(11) DEFAULT NULL,
  `Tipo` char(5) COLLATE utf8_unicode_ci DEFAULT NULL,
  `Seccion` int(11) DEFAULT NULL,
  `RespuestaID` bigint(11) DEFAULT NULL,
  `Texto` text COLLATE utf8_unicode_ci ,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=522 ;

ดังนั้นค่าIDจะเพิ่มขึ้นโดยอัตโนมัติและฉันใช้ค่าคงที่ ('23') สำหรับEncuestaID.


ขอโทษฉันเพิ่งรู้ว่าคุณไม่ต้องการใช้ชื่อคอลัมน์จึงไม่สามารถใช้งานได้
Alex Angelico

1

ฉันต้องการสิ่งนี้เช่นกัน วิธีแก้ปัญหาของฉันคือใช้ SQLYOG (เวอร์ชันฟรี) เพื่อส่งออกบันทึกที่ต้องการเป็น SQL (สร้างส่วนแทรก)

จากนั้นฉันก็แก้ไขสิ่งนี้ด้วยมือเพื่อลบ id เนื่องจากต้องสร้างขึ้นโดยอัตโนมัติจากนั้นคัดลอกส่วนแทรกลงใน SQLYog เพื่อดำเนินการ สิ่งนี้ไม่เจ็บปวด ฉันเดาว่า MySQL GUI อื่น ๆ ก็สามารถทำได้เช่นกัน

สิ่งนี้ทำให้ฉันมีบันทึกที่ฉันสามารถใช้เพื่อวัตถุประสงค์ในการทดสอบบนระบบถ่ายทอดสด

ตอนนี้ฉันมีส่วนแทรกนี้สำหรับใช้ซ้ำแล้วเช่นกันเนื่องจากตารางจะถูกเขียนใหม่ทุกวัน


0

รูปแบบเล็กน้อยความแตกต่างหลักคือการตั้งค่าฟิลด์คีย์หลัก ("varname") เป็น null ซึ่งจะสร้างคำเตือน แต่ใช้งานได้ โดยการตั้งค่าคีย์หลักเป็น null การเพิ่มอัตโนมัติจะทำงานเมื่อแทรกระเบียนในคำสั่งสุดท้าย

รหัสนี้ยังล้างความพยายามก่อนหน้านี้และสามารถเรียกใช้มากกว่าหนึ่งครั้งโดยไม่มีปัญหา:

DELETE FROM `tbl` WHERE varname="primary key value for new record";
DROP TABLE tmp;
CREATE TEMPORARY TABLE tmp SELECT * FROM `tbl` WHERE varname="primary key value for old record";
UPDATE tmp SET varname=NULL;
INSERT INTO `tbl` SELECT * FROM tmp;

0

คุณสามารถปรับเปลี่ยนตารางชั่วคราวเพื่อเปลี่ยนฟิลด์ ID เป็น bigint หรือมากกว่านั้นโดยไม่มีข้อกำหนด NOT NULL จากนั้นตั้งค่า ID เป็น 0 ในตาราง temp นั้น หลังจากนั้นให้เพิ่มกลับไปที่ตารางเดิมและ NULL จะทริกเกอร์การเพิ่มอัตโนมัติ

CREATE TEMPORARY TABLE tmptable SELECT * FROM x WHERE (id='123');
ALTER TABLE tmptable CHANGE id id bigint;
UPDATE tmptable SET id = NULL;
INSERT INTO x SELECT * FROM tmptable;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.