在MySQL中,我能否复制一行并将其插入到同一张表中?

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

我想复制表中的一行数据并将其插入到同一张表里(即,我想要复制该表中的一个现有行),但我不想在“select”之后列出所有列,因为这个表有太多的列。

但是,当我尝试这样做时,我收到了以下错误:

重复条目'xxx',键为1

我可以通过创建另一个与原表具有相同列的临时容器来复制所需记录以避开这个问题:

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;

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


1
我只是对于硬编码的键值有些评论。我会采用类似于max(oldtable.id) + oldtable_temp.key的方式,这样我可以确保id递增且唯一。 - guy mograbi
可能是 在同一张 MySQL 表中复制记录 的重复问题。 - Organic Advocate
@OrganicAdvocate,这个问题有更多的答案和更多的浏览量,比那个问题要多。 - Drew
是的,不要使用update oldtable_tem set key=2;,而是使用update oldtable_tem set key=NULL;,然后简单地使用insert into oldtable select * from oldtable_tem; - Timofey Bugaevsky
26个回答

0

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

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

获取列名的其他选项:
-SHOW COLUMNS FROM tablename; (列名: Field)
-DESCRIBE tablename (列名: Field)
-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

我想分享一下我的 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();

0
这是我在这个网站上找到的答案,描述了如何完成上述操作1。你可以在页面底部找到答案。基本上,你需要将要复制的行复制到一个临时内存表中。然后使用update更改主键号码。接着将其重新插入目标表。最后删除该表。
以下是代码:

CREATE TEMPORARY TABLE rescueteam ENGINE=MEMORY SELECT * FROM fitnessreport4 WHERE rID=1;# 影响1行 UPDATE rescueteam SET rID=Null WHERE rID=1;# 影响1行 INSERT INTO fitnessreport4 SELECT * FROM rescueteam;# 影响1行 DROP TABLE rescueteam# MySQL 返回了空结果集(即零行)。

我创建了临时表rescueteam。我从原始表fitnessreport4中复制了行。然后,我将临时表中该行的主键设置为null,以便在将其复制回原始表时不会出现重复键错误。我昨晚尝试了这段代码,它可以正常工作。


0

max233在自增情况下肯定是正确的。但是不要使用ALTER TABLE。只需将临时表中的自增字段设置为NULL即可。这将会出现一个错误,但随后插入临时表中的所有字段将发生,并且NULL自动字段将获得唯一值。


0

这是对“Grim...”答案的补充解决方案。 有一些评论说它的主键为空。 有一些关于它不起作用的评论。 还有一些关于解决方案的评论。 没有一个解决方案适用于我们。我们使用带有InnoDB表的MariaDB。

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

来自“Grim...”的解决方案确实有效,但我们需要将PRIMARY KEY更改为UNIQUE。


0

参考上面以及 Stack Overflow 上的其他答案,我得出了以下代码作为克隆一条记录的最终版本:

CREATE TEMPORARY TABLE temptable SELECT * FROM things WHERE Thing_ID = 10000345;
UPDATE temptable SET Thing_ID = 0;
INSERT INTO things SELECT * FROM temptable;
DROP TEMPORARY TABLE IF EXISTS temptable;

将主键Thing_ID的值设置为0比更改临时表以允许NULL然后将主键设置为NULL要简短得多 - 对我而言,在MySQL中它起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接