在同一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行的,而不是复制行。有人知道如何解决这个问题吗?

// edit:我想在不键入所有字段名称的情况下执行此操作,因为字段名称会随着时间变化。

Answers:


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
如果要复制一条记录,则可以在更新和插入位置都删除where。然后,您可以在mysql控制台中按两次,这将调出历史记录中的倒数第二个操作,在更新中更改ID(最后方便),按Enter键,按一下,再次按Enter键而不更改任何内容,然后重复多份复印的过程。快多了。
Cristian Vrabie 2012年

4
如果id 100已经存在的话该怎么办。其次,如果您为新行选择任何新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

@TobiasBeuvingColumn 'id' connot be null
Marcel

83

在多客户端环境中,Alex的答案需要格外小心(例如锁定或事务处理)。

假设该AUTO ID字段是表中的第一个字段(通常情况),我们可以使用隐式事务。

    从发票WHERE ...;中创建临时表tmp SELECT *;
    ALTER TABLE tmp drop ID;#drop autoincrement字段
    #更新tmp SET ...; #只需更改其他唯一键
    INSERT INTO发票SELECT 0,tmp。* FROM tmp;
    DROP TABLE tmp;

从MySQL文档:

使用AUTO_INCREMENT:您还可以为该列显式分配NULL或0以生成序列号。


即使我有一个AUTO_INCREMENT字段,这也将记录的ID设置为0。将其更改为null可行,因此,我建议您将代码更改为使用null而不是0。我使用的是MySQL 5.5.35(Ubuntu)。
Tyler Collier 2014年

@TylerCollier:NULL0两者都可接受。您是否可以通过复制并在此处分享问题的屏幕截图?
Ravinder Reddy 2014年

我的大脑在尝试理解第4行中的neato子查询时很痛苦。也许此解释有所帮助:目标表invoices包含一个AUTO_INCREMENT ID字段。源表tmp没有。为了在插入过程中获得所需的1:1相关性,您可以将a指定0为select的第一个字段,即目标表的AUTO_INCREMENT字段。在tmp.*选择所有从源表中的字段tmp。完善!您现在具有1:1关联。很棒的答案@Tim。
elbowlobstercowstand

@RavinderReddy的NULL和0不相同-可能有一个记录ID 0-对这种情况使用NULL
Tobias Beuving

@TobiasBeuving:我清楚地知道,NULL并且0是不一样的。当前上下文已启用AUTO_INCREMENT。您不能在此类字段中插入0NULL值。但是当你把它们作为投入这样的字段,一个序列值将被分配到该字段自动 matically。因此,我的评论。关于SQL Fiddle的证明:sqlfiddle.com/#
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个字段,它们可能会随时间变化)。我不想每次tablestructre更改时都更改代码。
Digits

您必须。您最好制作一个从字段列表生成SQL语句的脚本。这很琐碎。
Ingo 2009年

唯一的其他选择是使用插入触发器(如果mysql支持)。
Ingo 2009年

11

您的方法很好,但是问题是您使用“ *”代替了字段名。如果将所有列名称都放在excep主键上,则脚本将在一条或多条记录上像魅力一样工作。

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


但是,这意味着您必须在表中添加或删除字段后立即更改代码,我个人认为这是不行的。(并非总是如此,但经常是足够的。)
Roemer

9

我知道一个较晚的答案,但它仍然是一个常见问题,我想添加另一个答案,它仅对单个行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_INCREMENTid列,否则,您也可以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 '17

是的。对我来说是个好主意。谢谢!!
Ragu Natarajan

3

我只是想扩展一下Alex的绝妙答案,以便在您碰巧要复制整个记录集时使其适用:

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

我只需要这样做,就发现亚历克斯的答案是一个完美的起点!当然,您必须将@x设置为表中的最高行号(我敢肯定您可以通过查询来获取它)。这仅在这种非常特殊的情况下才有用,因此当您不希望重复所有行时,请小心使用。根据需要调整数学。


2

我有一个类似的问题,这就是我在做什么:

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

被普里贡塔斯:

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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.