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

lin*_*ina 149 mysql copy row duplicates

insert into table select * from table where primarykey=1
Run Code Online (Sandbox Code Playgroud)

我只想复制一行插入到同一个表中(即,我想复制表中的现有行)但我想这样做而不必列出"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;
Run Code Online (Sandbox Code Playgroud)

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

Gri*_*... 183

我使用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;
Run Code Online (Sandbox Code Playgroud)

作为临时表,永远不应该有多个记录,因此您不必担心主键.将其设置为null允许MySQL自己选择值,因此不存在创建重复的风险.

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

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

  • 主键如何为空? (9认同)
  • 如果它为空,则在插入时自动为其分配下一个AI编号. (4认同)
  • 这个解决方案是正确的(与其他一些获得投票的方案相比),因为它允许MySQL选择主键值. (3认同)
  • 如果表的架构不允许主键中存在 NULL,请将其设置为 0 而不是 NULL。 (2认同)
  • IMRAN-您可能会问。我以为临时表的id字段不是自动创建的PK(我很确定SQL Server在这种情况下不会这样做),但是确实如此。我需要做ALTER TABLE tmptable_1修改`primarykey` INT NULL; 在第一行之后使解决方案起作用 (2认同)

Leo*_*lis 57

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;
Run Code Online (Sandbox Code Playgroud)

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

  • 这与OP已经为他们自己的问题提供的解决方案基本相同,尽管有_actual_临时表,而且创建模式略微清晰.我认为OP要求他们已经使用了一个更好的方法,而不是清理他们现有的语法.不要真正理解所有的赞成. (16认同)

Jor*_*ing 37

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

INSERT INTO table (col1, col2, col3, ...)
SELECT col1, col2, col3, ... FROM table
  WHERE primarykey = 1
Run Code Online (Sandbox Code Playgroud)

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

如果它不是一个AUTO_INCREMENT列,并且您希望能够为primarykey它选择相似的新值:

INSERT INTO table (primarykey, col2, col3, ...)
SELECT 567, col2, col3, ... FROM table
  WHERE primarykey = 1
Run Code Online (Sandbox Code Playgroud)

... ... 567的新值primarykey.

  • 当忽略这个问题时,为什么会被投票7次呢?*...但是我不希望列出"select"之后的所有列,因为这个表有太多列...* (40认同)
  • -1这正是OP想要避免的.阅读问题,或至少提供一个答案,如"这是唯一可能的方式". (7认同)
  • 因为有时问题本身是错误的.:)在不改变内容的情况下复制行会增加冗余.如果可能,应建议更改数据库结构以最小化冗余. (5认同)
  • 因为您可能不希望与OP完全相同而登陆此页面 (5认同)
  • 仅仅因为在正常做法下它是不对的并没有错.很多时候标准的最佳实践不适用.示例:数据库表示文档,用户需要在进行更改之前保留文档的副本. (4认同)

小智 12

您几乎已经使用了第一个查询,只需要指定列,这样您就可以在插入中排除主键,这将生成您可能在表上自动增加的自动增量,以便为表创建新的主键.条目.

例如改变这个:

insert into table select * from table where primarykey=1
Run Code Online (Sandbox Code Playgroud)

对此:

INSERT INTO table (col1, col2, col3) 
SELECT col1, col2, col3 
FROM table 
WHERE primarykey = 1
Run Code Online (Sandbox Code Playgroud)

只是不要在INSERT的列列表或查询的SELECT部分​​中包含primarykey列.


don*_*lly 9

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

mysqldump -umyuser -p mydatabase --skip-extended-insert mytable > outfile.sql
Run Code Online (Sandbox Code Playgroud)

--skip-extended-insert给你每行一个INSERT命令.然后,您可以在您喜欢的文本编辑器中找到该行,解压缩命令并将主键更改为"default".

  • 来自我的+1,因为这是一个很好的"开箱即用"方法,在某些情况下,在运行时之外实际上可能会有所帮助.但主要是它是实际解决OP问题的第一个答案,并不只是在语法上调整OP已经提供的解决方案. (2认同)

cur*_*mil 7

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

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;
Run Code Online (Sandbox Code Playgroud)

这将导致新行获得自动递增的 id,而不是来自所选行的 id。


Pio*_*rek 6

此过程假定:

  • 你没有_duplicate_temp_table
  • 你的主键是int
  • 您可以访问create table

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

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;
Run Code Online (Sandbox Code Playgroud)


小智 5

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

INSERT INTO `test_tbl` (`name`,`age`) SELECT `name`,`age` FROM `test_tbl`;
Run Code Online (Sandbox Code Playgroud)

此查询导致复制每一行。


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

INSERT INTO `test_tbl` (`id`,`name`,`age`)
  SELECT 20,`name`,`age` FROM `test_tbl` WHERE id = 19;
Run Code Online (Sandbox Code Playgroud)

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


Luc*_*llo 5

我使用了 Grim 的技术,做了一点改变:如果有人寻找这个查询是因为由于主键问题而无法执行简单的查询:

INSERT INTO table SELECT * FROM table WHERE primakey=1;
Run Code Online (Sandbox Code Playgroud)

在我的 MySql 安装 5.6.26 中,密钥不可为空并产生错误:

#1048 - Column 'primakey' cannot be null 
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这实际上用 InnoDB 表救了我的命! (2认同)