在MySQL中,我可以复制一行以插入同一张表吗?


162
insert into table select * from table where primarykey=1

我只想复制一行以插入到同一表中(即,我想复制表中的现有行),但是我想这样做而不必在“选择”之后列出所有列,因为该表具有列过多。

但是当我这样做时,我得到了错误:

密钥1的条目“ xxx”重复

我可以通过创建另一个表来创建一个表,该表具有与要复制的记录的临时容器相同的列:

create table oldtable_temp like oldtable;
insert into oldtable_temp select * from oldtable where key=1;
update oldtable_tem set key=2;
insert into oldtable select * from oldtable where key=2;

有没有更简单的方法来解决这个问题?


我只是对密钥的硬编码值发表评论。我将以max(oldtable.id) + oldtable_temp.key这种方式进行操作,以确保ID递增并且是唯一的。
Guy Mograbi


@OrganicAdvocate这个问题比这个问题有更多答案和更多观点
Drew

Answers:


189

我使用Leonard Challis的技术进行了一些更改:

CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM table WHERE primarykey = 1;
UPDATE tmptable_1 SET primarykey = NULL;
INSERT INTO table SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;

作为临时表,记录永远不应该超过一个,因此您不必担心主键。将其设置为null允许MySQL选择值本身,因此没有创建重复项的风险。

如果您想确保自己只插入一行,可以在INSERT INTO行的末尾添加LIMIT 1。

请注意,我还将主键值(在本例中为1)附加到了我的临时表名称上。


3
该解决方案是正确的(与其他获得投票的解决方案相比),因为它允许MySQL选择主键值。
Jan Hettich

1
尽管由于临时表名称不一致(tmptable_1vs. tmptable)而不能完全正常工作
Kieran Tully

9
主键如何才能为空?
Imran Shafqat 2013年

5
如果为空,则在插入时会自动为其分配下一个AI号。
Grim ...

2
IMRAN-您可能会问。我以为临时表的id字段不是自动设置为PK(我很确定SQL Server在这种情况下不会这样做),但是确实如此。我需要做ALTER TABLE tmptable_1 MODIFY primarykeyINT NULL; 在使解决方案起作用的第一行之后
DJDave

61

2014年7月7日更新-基于我的答案(由Grim ...给出的答案)是一个更好的解决方案,因为它对下面的解决方案有所改进,所以我建议使用该解决方案。

您可以执行以下操作,而无需使用以下语法列出所有列:

CREATE TEMPORARY TABLE tmptable SELECT * FROM table WHERE primarykey = 1;
UPDATE tmptable SET primarykey = 2 WHERE primarykey = 1;
INSERT INTO table SELECT * FROM tmptable WHERE primarykey = 2;

您可以决定以其他方式更改主键。


16
这基本上与OP为解决自己的问题而提供的解决方案相同,尽管使用了实际的临时表,并且创建模式略为简洁。我认为OP要求对他们已在使用的内容采用更好的方法,而不是清理其现有语法。不太了解所有对此的支持。
Sepster 2012年

谢谢。它具有动态功能的顺利
SONAL Khunt

此过程完成后,是否有必要删除tmptable?
SSH,

“默认情况下,当数据库连接终止时,所有临时表都会被MySQL删除。您仍然希望在这两个临时表之间删除它们,然后发出DROP TABLE命令来删除它们。” 这里的更多内容
LeonardChallis

既然创建了tmptable,就不需要WHERE primarykey = 2在最后一行添加语句。(即INSERT INTO table SELECT * FROM tmptable。)
Marcus

42

我假设您希望新记录有新记录primarykey?如果primarykey是,AUTO_INCREMENT那就这样做:

INSERT INTO table (col1, col2, col3, ...)
SELECT col1, col2, col3, ... FROM table
  WHERE primarykey = 1

... col1, col2, col3, ...表格中除以外的 所有列都在哪里primarykey

如果它不是AUTO_INCREMENT列,并且您希望能够选择新值,primarykey则类似于:

INSERT INTO table (primarykey, col2, col3, ...)
SELECT 567, col2, col3, ... FROM table
  WHERE primarykey = 1

... 567的新值在哪里primarykey


41
当它忽略这个问题时,为什么要对它进行7次投票?...但是我不想在“选择”之后列出所有列,因为该表有太多列...
LeonardChallis

7
-1,因为这正是OP想要避免的。阅读问题,或者至少提供诸如“这是唯一可能的方式”之类的答案。
devios1 2012年

5
因为有时问题本身是错误的。:)复制行而不更改内容会增加冗余。如果可能,建议更改数据库结构以最大程度地减少冗余。
Torben 2013年

5
仅仅因为在正常实践中这是不对的,就不会出错。在很多情况下,标准最佳实践都不适用。示例:数据库代表文档,并且用户需要在进行更改之前保留文档的副本。
2013年

6
因为您可能不希望与该OP完全相同,所以会落在此页面上
jx12345

13

您在第一个查询中几乎就需要指定列,因此您可以在插入中排除主键,这将使表上可能具有的自动增量功能自动为表创建新的主键。条目。

例如更改此:

insert into table select * from table where primarykey=1

对此:

INSERT INTO table (col1, col2, col3) 
SELECT col1, col2, col3 
FROM table 
WHERE primarykey = 1

只是不要在查询的INSERT或SELECT部分​​的列列表中包括主键列。


1
有没有办法通过select语句拉动大多数列,但在insert命令中手动更新其中的一列?例如insert into table ("val1", col2, col3) select col2, col3 from table where primarykey = 1还是类似的东西?
anon58192932 '18 -10-25

1
找到了!从一列中的值复制,但是添加自己的价值观之一到插入语句:stackoverflow.com/questions/23971078/...
anon58192932

1
抱歉,您的问题刚刚得到通知,很高兴您找到了,确实是这样。
Braeden Black '18

9

您也可以尝试转储表,找到insert命令并对其进行编辑:

mysqldump -umyuser -p mydatabase --skip-extended-insert mytable > outfile.sql

--skip-extended-insert给你每行一个INSERT命令。然后,您可以在您喜欢的文本编辑器中找到该行,提取命令并将主键更改为“默认”。


2
向我+1,因为这是一种很好的“开箱即用”方法,在某些情况下,在运行时之外可能实际上会有所帮助。但是首先,这是真正解决OP问题的第一个答案,而不仅仅是在语法上调整OP已经提供的解决方案。
Sepster 2012年

6

此过程假定:

  • 您没有_duplicate_temp_table
  • 您的主键是int
  • 您有权创建表

当然,这并不是完美的,但是在某些(可能是大多数)情况下,它会起作用。

DELIMITER $$
CREATE PROCEDURE DUPLICATE_ROW(copytable VARCHAR(255), primarykey VARCHAR(255), copyid INT, out newid INT)
BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @error=1;
        SET @temptable = '_duplicate_temp_table';
        SET @sql_text = CONCAT('CREATE TABLE ', @temptable, ' LIKE ', copytable);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('INSERT INTO ', @temptable, ' SELECT * FROM ', copytable, ' where ', primarykey,'=', copyid);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('SELECT max(', primarykey, ')+1 FROM ', copytable, ' INTO @newid');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('UPDATE ', @temptable, ' SET ', primarykey, '=@newid');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('INSERT INTO ', copytable, ' SELECT * FROM ', @temptable, '');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('DROP TABLE ', @temptable);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SELECT @newid INTO newid;
END $$
DELIMITER ;

CALL DUPLICATE_ROW('table', 'primarykey', 1, @duplicate_id);
SELECT @duplicate_id;

1
+1尚未确认,works但方法是合理的,并且很有价值,因为它在这里是唯一的,并且确实解决了OP的问题。
Sepster 2012年

5

这可以通过一些创造力来实现:

SET @sql = CONCAT('INSERT INTO <table> SELECT null, 
    ', (SELECT GROUP_CONCAT(COLUMN_NAME) 
    FROM information_schema.columns 
    WHERE table_schema = '<database>' 
    AND table_name = '<table>' 
    AND column_name NOT IN ('id')), ' 
from <table> WHERE id = <id>');  

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;

这将导致新行获得一个自动递增的ID,而不是所选行中的ID。


这应该是公认的答案!
Felipe Desiderati

5

如果表的主键字段是自动递增字段,则可以对列使用查询。例如,您名为的表test_tbl具有3个字段,如id, name, ageid是主键字段并自动递增,因此您可以使用以下查询复制该行:

INSERT INTO `test_tbl` (`name`,`age`) SELECT `name`,`age` FROM `test_tbl`;

此查询导致复制每一行。


如果表的主键字段不是自动递增字段,则可以使用以下方法:

INSERT INTO `test_tbl` (`id`,`name`,`age`)
  SELECT 20,`name`,`age` FROM `test_tbl` WHERE id = 19;

该查询的结果是作为id=19插入的重复行id=20


这是最好的答案存在,情况是非常简单的:INSERT INTO tableNamefieldName1fieldName2fieldName3)SELECT fieldName1fieldName2fieldName3FROM tableNameWHERE 1(复制所有这些一次)
jakubplus

其次-这个答案是简洁的,本着实际上“复制”一行的精神。
user37309 '19

4

克隆具有更新字段和自动增量值的行

CREATE TEMPORARY TABLE `temp` SELECT * FROM `testing` WHERE id = 14;

UPDATE `temp` SET id = (SELECT id FROM testing ORDER by id DESC LIMIT 1
 )+1, user_id = 252 ,policy_no = "mysdddd12" where id = 14;

INSERT INTO `testing` SELECT * FROM `temp`;

DROP TEMPORARY TABLE IF EXISTS `temp`;

@Torben请检查问题左侧的对勾(将其标记为已接受),以便每个人都知道它对您有用。
HosseyNJF

3

以下是本网站的内容。这是我在具有任意多个字段的表中复制记录的方法:

这还假设您在表的开头有一个AI字段

function duplicateRow( $id = 1 ){
dbLink();//my db connection
$qColumnNames = mysql_query("SHOW COLUMNS FROM table") or die("mysql error");
$numColumns = mysql_num_rows($qColumnNames);

for ($x = 0;$x < $numColumns;$x++){
$colname[] = mysql_fetch_row($qColumnNames);
}

$sql = "SELECT * FROM table WHERE tableId = '$id'";
$row = mysql_fetch_row(mysql_query($sql));
$sql = "INSERT INTO table SET ";
for($i=1;$i<count($colname)-4;$i++){//i set to 1 to preclude the id field
//we set count($colname)-4 to avoid the last 4 fields (good for our implementation)
$sql .= "`".$colname[$i][0]."`  =  '".$row[$i]. "', ";
}
$sql .= " CreateTime = NOW()";// we need the new record to have a new timestamp
mysql_query($sql);
$sql = "SELECT MAX(tableId) FROM table";
$res = mysql_query($sql);
$row = mysql_fetch_row($res);
return $row[0];//gives the new ID from auto incrementing
}

2

我可能迟到了,但是我有一个对我有用的类似解决方案。

 INSERT INTO `orders` SELECT MAX(`order_id`)+1,`container_id`, `order_date`, `receive_date`, `timestamp` FROM `orders` WHERE `order_id` = 1

这样,我不需要创建临时表等。由于将行复制到同一表中,Max(PK)+1因此可以轻松使用该函数。

我来寻找这个问题的解决方案(忘记了语法),最后我做了自己的查询。有趣的是有些时候事情如何进行。

问候


2

如果主键是自动增量,则只需指定除主键之外的每个字段。

INSERT INTO table(field1,field2,field3) SELECT (field1,field2,field3) FROM table WHERE primarykey=1


2

我更新了@LeonardChallis的解决方案,因为它对我不起作用,其他解决方案都不对。我删除了WHERE子句和SET primaryKey = 0temp表中的内容,以便MySQL自身自动递增primaryKey

CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable;
UPDATE tmptable SET primaryKey = 0;
INSERT INTO myTable SELECT * FROM tmptable;

当然,这是要复制表中的所有行。


2

我使用了Grim的技术,但做了一点改动:如果某人正在寻找此查询是因为主键问题导致无法进行简单查询:

INSERT INTO table SELECT * FROM table WHERE primakey=1;

使用MySql安装5.6.26,密钥不可为空,并产生错误:

#1048 - Column 'primakey' cannot be null 

因此,在创建临时表之后,我将主键更改为可为空。

CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM table WHERE primarykey = 1;
ALTER TABLE tmptable_1 MODIFY primarykey int(12) null;
UPDATE tmptable_1 SET primarykey = NULL;
INSERT INTO table SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;

或者,在mariadb / mysql的新版本中,primaryKey可以设置为0,这样在插入时允许自动增量工作。
shelbypereira

2

我会在下面使用

insert into ORDER_ITEM select * from ORDER_ITEM where ITEM_NUMBER =123;

1
如果ITEM_NUMBER是您的主键,则此方法不起作用,这很可能在这种情况下。
比利·皮格里姆

2

我在Koha数据库中用于在条形码列中插入带有“ C”前缀的重复项:

INSERT INTO items (`biblionumber`, `biblioitemnumber`, `barcode`, `dateaccessioned` ) SELECT `biblionumber`, `biblioitemnumber`,  CONCAT('C',`barcode`), `dateaccessioned` FROM `items` WHERE barcode='14832';

1

我只需要这样做,这是我的手动解决方案:

  1. phpmyadmin中,选中要复制的行
  2. 在查询结果操作下的底部,单击“导出”
  3. 在下一页上,选中“另存为文件”,然后单击“转到”
  4. 使用文本编辑器打开导出的文件,找到主字段的值并将其更改为唯一的值。
  5. 返回phpmyadmin中,单击“导入”选项卡,在浏览器下找到要导入.sql文件的文件,单击“执行”,然后应插入重复的行。

如果您不知道PRIMARY字段是什么,请回头查看phpmyadmin页面,单击“ Structure”选项卡,然后在页面底部的“ Indexes”下,将向您显示哪个“ Field”具有“ Keyname”'PRIMARY'

有很长一段路要走,但是如果您不想处理标记,只需要复制一行就可以了。


好的,以一种开箱即用的方式回答操作问题。显然,并非在所有情况下都适用,例如斑点和地理空间数据,但很容易实现,并且确实适用于那些奇怪的球变。
Strixy

1

上面显示的解决方案对于选定的行也很完美。例如,我正在为nice2work项目创建演示行,这很完美。

CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable WHERE id=500;
UPDATE tmptable SET id = 0;
UPDATE some fields I need to change
INSERT INTO myTable SELECT * FROM tmptable;
DROP TABLE tmptable;

//  You can use this same also directly into your code like (PHP Style)
$sql = "CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable WHERE id=500;
UPDATE tmptable SET id = 0;
UPDATE some fields I need to change
INSERT INTO myTable SELECT * FROM tmptable;DROP TABLE tmptable;";

0

对不起,这是我的死灵书,但这是我在google上发现的东西,因为我发现这很有帮助,但有问题,因此我想为其他挖出这个东西的人做出重要的修改。

首先,我使用的是SQL Server,而不是MySQL,但我认为它的工作原理类似。我使用Leonard Challis的解决方案是因为它最简单并且可以满足需求,但是这有一个问题-如果仅采用PK并将其递增1,那么自从有问题的行被添加以来,如果您添加了其他记录,会发生什么情况? 。我认为最好只让系统处理PK的自动增量,因此我做了以下工作:

SELECT * INTO #tmpTable FROM Table WHERE primarykey = 1
--Optionally you can modify one or more fields here like this: 
--UPDATE #tmpTable SET somefield = newData
ALTER TABLE #tmpTable DROP COLUMN TicketUpdateID
INSERT INTO Tickets SELECT * FROM #tmpTable
DROP TABLE #tmpTable

我相信这在MySQL中会类似地工作,但是我无法对此进行测试,对不起


1
完全是bakwaas的答案
AsadYarKhan 2013年

0

我知道这是一个老问题,但这是另一个解决方案:

假设主键是自动递增的,这将在主表中复制一行,并使用新的主表ID创建链接表数据的副本。

获取列名称的其他选项:
-SHOW COLUMNS FROM tablename; (列名:字段)
-DESCRIBE tablename (列名:字段)
-SELECT column_name FROM information_schema.columns WHERE table_name ='tablename'(列名:column_name)

//First, copy main_table row
$ColumnHdr='';
$Query="SHOW COLUMNS FROM `main_table`;";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
while($Row=mysql_fetch_array($Result))
{
    if($Row['Field']=='MainTableID')     //skip main table id in column list
        continue;
    $ColumnHdr.=",`" . $Row['Field'] . "`";
}
$Query="INSERT INTO `main_table` (" . substr($ColumnHdr,1) . ")
        (SELECT " . substr($ColumnHdr,1) . " FROM `main_table`
            WHERE `MainTableID`=" . $OldMainTableID . ");";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
$NewMainTableID=mysql_insert_id($link);

//Change the name (assumes a 30 char field)
$Query="UPDATE `main_table` SET `Title`=CONCAT(SUBSTRING(`Title`,1,25),' Copy') WHERE `MainTableID`=" . $NewMainTableID . ";";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);

//now copy in the linked tables
$TableArr=array("main_table_link1","main_table_link2","main_table_link3");
foreach($TableArr as $TableArrK=>$TableArrV)
{
    $ColumnHdr='';
    $Query="SHOW COLUMNS FROM `" . $TableArrV . "`;";
    $Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
    while($Row=mysql_fetch_array($Result))
    {
        if($Row['Field']=='MainTableID')     //skip main table id in column list, re-added in query
            continue;
        if($Row['Field']=='dbID')    //skip auto-increment,primary key in linked table
            continue;
        $ColumnHdr.=",`" . $Row['Field'] . "`";
    }

    $Query="INSERT INTO `" . $TableArrV . "` (`MainTableID`," . substr($ColumnHdr,1) . ")
            (SELECT " . $NewMainTableID . "," . substr($ColumnHdr,1) . " FROM `" . $TableArrV . "`
             WHERE `MainTableID`=" . $OldMainTableID . ");";
    $Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
}

0

至少对于自动增量而言,max233无疑是正确的。但是,请勿执行ALTER TABLE。只需将临时表中的auto-increment字段设置为NULL。这将出现错误,但是临时表中所有字段的后续INSERT都会发生,并且NULL自动字段将获得唯一值。


0

建立表格

    CREATE TABLE `sample_table` (
       `sample_id` INT(10) unsigned NOT NULL AUTO_INCREMENT,
       `sample_name` VARCHAR(255) NOT NULL,
       `sample_col_1` TINYINT(1) NOT NULL,
       `sample_col_2` TINYINT(2) NOT NULL,

      PRIMARY KEY (`sample_id`),
      UNIQUE KEY `sample_id` (`sample_id`)

    ) ENGINE='InnoDB' DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

插入一行

INSERT INTO `sample_table`
   VALUES(NULL, 'sample name', 1, 2);

克隆行上方插入

INSERT INTO `sample_table`
   SELECT 
    NULL AS `sample_id`, -- new AUTO_INCREMENT PRIMARY KEY from MySQL
    'new dummy entry' AS `sample_name`,  -- new UNIQUE KEY from you
    `sample_col_1`, -- col from old row
    `sample_col_2` -- col from old row
   FROM `sample_table`
   WHERE `sample_id` = 1;

测试

SELECT * FROM `sample_table`;

注意:如果遇到错误(#1048),请使用''代替NULL
AbdullahAydın15

仅仅是我还是这正是 OP 所不想要的?(即,手动将每列添加到查询中。)
Byson 2016年

没错,我只是读过OP的“我只想复制一行以插入到同一张表中”这句话,也许,无论如何,这对于“键???的重复条目'xxx'还是有用的” 错误^^
AbdullahAydın2016年

0

这是我在此网站上在线找到的答案,描述了如何进行上述操作1 找到的答案,您可以在页面底部找到答案。基本上,您要做的就是将要复制的行复制到内存中的临时表中。然后,您可以使用update更改主键号。然后,将其重新插入到目标表中。然后,您放下桌子。

这是它的代码:

CREATE TEMPORARY TABLE rescueteamENGINE = MEMORY SELECT * FROM fitnessreport4WHERE rID = 1;#1行受影响。UPDATE rescueteamSET rID = Null WHERE rID = 1;#1行受影响.INSERT INTO fitnessreport4SELECT * FROM rescueteam;#1行受影响。DROP TABLE rescueteam#MySQL返回一个空结果集(即零
行)。

我创建了临时表救援队。我从原始表Fitnessreport4复制了该行。然后,我将临时表中该行的主键设置为null,以便可以将其复制回原始表而不会出现“重复键”错误。我昨天晚上试过了这段代码,它奏效了。


0

这是对“ Grim ...”答案的另一种解决方案,对此有一些评论,其主键为null。关于它的一些评论不起作用。以及对解决方案的一些评论。没有一种解决方案对我们有用。我们有带有InnoDB表的MariaDB。

我们无法将主键设置为允许null。使用0代替NULL会导致主键重复值错误。 SET SQL_SAFE_UPDATES = 0;也没有工作。

如果我们将PRIMARY KEY改为UNIQUE,那么“ Grim ...”解决方案就可以了


-1

只是想发布我的PHP代码,因为我认为我收集列的方式比以前的示例在代码上更简洁。这也显示了如何轻松地更改字段,在这种情况下添加字符串。但是,如果您还想复制一些子记录,也可以用新添加的记录替换外键字段。

  // Read columns, unset the PK (always the first field in my case)
  $stmt = $conn->prepare('SHOW COLUMNS FROM template');
  $stmt->execute();

  $columns = $stmt->fetchAll();
  $columns = array_map(function ($element) { return $element['Field']; }, $columns);

  unset($columns[0]);

  // Insert record in the database. Add string COPY to the name field.
  $sql = "INSERT INTO `template` (".implode(",", $columns).")";
  if ($key = array_search('name', $columns))
      $columns[$key] = "CONCAT(name, ' COPY')";
  $sql .= " SELECT ".implode(",", $columns)." FROM `template` WHERE `id` = ".$id;

  $stmt = $conn->prepare($sql);
  $stmt->execute();

-2

对于一个非常简单的解决方案,您可以使用PHPMyAdmin将行导出为CSV文件,然后只需导入修改后的CSV文件。导入之前,编辑ID /主键列以使其不显示任何值。

SELECT * FROM table where primarykey=1

然后在页面底部:

在此处输入图片说明

哪里说“导出”只是导出,然后编辑csv文件以删除主键值,所以它为空,然后将其导入数据库,导入时将分配一个新的主键。

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.