В MySQL я могу скопировать одну строку, чтобы вставить в ту же таблицу? - PullRequest
143 голосов
/ 28 октября 2010
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;

Есть ли более простой способ решить эту проблему?

Ответы [ 25 ]

174 голосов
/ 20 сентября 2012

Я использовал технику Леонарда Чаллиса с несколькими изменениями:

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) к имени временной таблицы.

57 голосов
/ 02 мая 2012

Обновление 07/07/2014 - Ответ, основанный на моем ответе Мрачно ... , является лучшим решением, поскольку он улучшает мое решение ниже, поэтому я бы предложил использоватьчто.

Вы можете сделать это без перечисления всех столбцов со следующим синтаксисом:

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;

Вы можете решить изменить первичный ключ другим способом.

37 голосов
/ 28 октября 2010

Я предполагаю, что вы хотите, чтобы новая запись имела новый 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.

12 голосов
/ 13 декабря 2016

У вас почти получилось с первым запросом, вам просто нужно указать столбцы. Таким образом, вы можете исключить свой первичный ключ из вставки, который применит автоматическое приращение, которое вы, вероятно, имеете в таблице, для автоматического создания нового первичногоключ для записи.

Например, измените это:

insert into table select * from table where primarykey=1

На это:

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

Просто не включайте столбец primarykey ни в один список столбцовдля INSERT или для SELECT частей запроса.

8 голосов
/ 17 июня 2012

Вы также можете попробовать выгрузить таблицу, найти команду вставки и отредактировать ее:

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

--skip-extended-insert дает вам одну команду вставки на строку. Затем вы можете найти строку в вашем любимом текстовом редакторе, извлечь команду и изменить первичный ключ на «default».

6 голосов
/ 25 августа 2012

Эта процедура предполагает, что:

  • у вас нет _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;
4 голосов
/ 31 мая 2014

Это может быть достигнуто с некоторой креативностью:

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;

Это приведет к тому, что новая строка получит автоматически увеличенный идентификатор вместо идентификатора из выбранной строки.

4 голосов
/ 30 июня 2014

Если поле первичного ключа вашей таблицы является полем с автоинкрементом, то вы можете использовать запрос со столбцами.Например, ваша таблица с именем test_tbl имеет 3 поля как id, name, age.id является полем первичного ключа и автоматическим приращением, поэтому вы можете использовать следующий запрос для дублирования строки:

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.

3 голосов
/ 08 декабря 2017

строка клона с полями обновления и значением автоинкремента

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`;
3 голосов
/ 08 декабря 2012

Некоторые из следующего были почерпнуты из этого сайта.Это то, что я сделал, чтобы дублировать запись в таблице с любым количеством полей:

Это также предполагает, что у вас есть поле 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
}
...