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

时间:2010-10-28 04:24:35

标签: mysql copy duplicates row

insert into table select * from table where primarykey=1

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

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

  

密钥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;

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

27 个答案:

答案 0 :(得分:172)

我使用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自己选择值,因此不存在创建重复的风险。

如果你想要确定你只需要插入一行,你可以将LIMIT 1添加到INSERT INTO行的末尾。

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

答案 1 :(得分:56)

更新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;

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

答案 2 :(得分:36)

我假设您希望新记录有新的primarykey?如果primarykeyAUTO_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

...其中567primarykey的新值。

答案 3 :(得分:12)

您几乎已经使用了第一个查询,只需要指定列,这样您就可以在插入中排除主键,这将导致您可能在表上自动增加以自动创建新的主键进入的关键。

例如改变这个:

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部分​​中包含primarykey列。

答案 4 :(得分:8)

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

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

--skip-extended-insert每行为您提供一个插入命令。然后,您可以在您喜欢的文本编辑器中找到该行,解压缩命令并将主键更改为“default”。

答案 5 :(得分: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;

答案 6 :(得分:4)

如果表的主键字段是自动增量字段,则可以对列使用查询。例如,名为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的重复行。

答案 7 :(得分:4)

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

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。

答案 8 :(得分: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
}

答案 9 :(得分:2)

我更新了@ LeonardChallis的解决方案,因为它对我没有任何影响。我删除了临时表中的WHERE子句和SET primaryKey = 0,以便MySQL自动增加primaryKey

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

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

答案 10 :(得分:2)

使用更新字段和自动增量值克隆行

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`;

答案 11 :(得分:2)

我会在下面使用,

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

答案 12 :(得分:2)

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

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

答案 13 :(得分: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函数可以很容易地使用。

我来寻找这个问题的解决方案(忘记了语法),最后我自己创建了查询。有趣的是,事情有时会发挥作用。

此致

答案 14 :(得分:1)

我使用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;

答案 15 :(得分: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;";

答案 16 :(得分:1)

我必须这样做,这是我的手动解决方案:

  1. phpmyadmin 中,检查您要复制的行
  2. 在查询结果操作底部,点击&#39;导出&#39;
  3. 在下一页上,选中&#39;另存为文件&#39; ,然后点击&#39; Go&#39;
  4. 使用文本编辑器打开导出的文件,找到主要字段的值并将其更改为唯一的字段。
  5. 返回 phpmyadmin ,点击&#39;导入&#39; 标签,找到要导入浏览下的.sql文件的文件,点击&#39; Go&#39; ,然后插入重复的行。
  6. 如果您不知道 PRIMARY 字段是什么,请回顾 phpmyadmin 页面,点击&#39;结构&# 39; 标签位于&#39;索引&#39; 下的页面底部,它会显示&#39;字段&#39; 有一个&#39; Keyname&#39; &#39; PRIMARY&#39;

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

答案 17 :(得分:1)

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

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

答案 18 :(得分:0)

max233肯定是在正确的轨道上,至少对于自动增量案例。 但是,不要做ALTER TABLE。只需在中设置自动增量字段即可 临时表为NULL。这将出现错误,但以下INSERT 临时表中的所有字段都将发生,NULL auto字段将获得 一个独特的价值。

答案 19 :(得分: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`;

答案 20 :(得分: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 row affected.INSERT   INTO fitnessreport4 SELECT * FROM rescueteam;#1行受影响。   DROP TABLE rescueteam #MySQL返回一个空结果集(即零
      行)。

我创建了临时表rescueteam。我复制了原始表fitnessreport4中的行。然后,我将临时表中的行的主键设置为null,以便我可以将其复制回原始表而不会出现Duplicate Key错误。我昨天晚上尝试了这段代码并且有效。

答案 21 :(得分:0)

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

//profile, lenght, width, qty, color//
r2,500,800,5,white
r2,200,100,2,white

然后在页面底部:

enter image description here

在哪里说&#34;出口&#34;只需导出,然后编辑csv文件以删除primarykey值,使其为空,然后将其导入数据库,导入时将分配一个新的主键。

答案 22 :(得分:0)

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

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

获取列名的其他选项:
-SHOW COLUMNS来自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__);
}

答案 23 :(得分:0)

对于necropost感到抱歉,但这是我在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中同样有效,但我无法测试,对不起

答案 24 :(得分:0)

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

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

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

答案 25 :(得分:0)

根据上面的答案以及 SO 上的其他地方,我将其作为克隆一条记录的最终代码:

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;

将 PRIMARY 键的值 Thing_ID 设置为 = 0 比更改临时表以允许 NULL 然后将 PRIMARY 键设置为 NULL 更短 - 对我来说,它适用于 MySQL。

答案 26 :(得分:-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();