Дублировать / копировать записи в одной таблице MySQL


87

Я долго искал, но не могу найти легкого решения своей проблемы. Я хотел бы продублировать запись в таблице, но, конечно, необходимо обновить уникальный первичный ключ.

У меня такой запрос:

INSERT INTO invoices
    SELECT * FROM invoices AS iv WHERE iv.ID=XXXXX
    ON DUPLICATE KEY UPDATE ID = (SELECT MAX(ID)+1 FROM invoices)

проблема в том, что это просто изменяет IDстроку, а не копирует строку. Кто-нибудь знает, как это исправить?

// edit: я хотел бы сделать это, не вводя все имена полей, потому что имена полей могут меняться со временем.

Ответы:


137

Обычно я использую временную таблицу. Вероятно, это неэффективно с вычислительной точки зрения, но, похоже, работает нормально! Здесь я полностью дублирую запись 99, создавая запись 100.

CREATE TEMPORARY TABLE tmp SELECT * FROM invoices WHERE id = 99;

UPDATE tmp SET id=100 WHERE id = 99;

INSERT INTO invoices SELECT * FROM tmp WHERE id = 100;

Надеюсь, это сработает для вас!


Мне это нравится, один шаг посередине, чтобы следить за происходящим,
Шон Дауни

4
Если вы копируете одну запись, вы можете удалить where как в update, так и в вставке. Затем вы можете просто дважды нажать вверх в консоли mysql, которая вызовет предпоследнюю операцию в истории, изменить идентификатор в обновлении, что удобно в конце, нажать ввод, нажать вверх, снова нажать ввод, ничего не меняя, затем повторить процедура создания нескольких копий. Намного быстрее.
Cristian Vrabie

4
что в случае, когда идентификатор 100 уже существует. Во-вторых, если вы выберете любой новый идентификатор для своей новой строки, и во время операции кто-то другой вставит новую строку, тогда ваш идентификатор и вновь вставленный идентификатор будут такими же. Это горлышко бутылки в этом коде.
nbhatti2001

6
Вы можете использовать NULL:CREATE TEMPORARY TABLE tmp SELECT bla FROM invoices WHERE bla; UPDATE tmp SET id=NULL; INSERT INTO invoices SELECT * FROM tmp;
Тобиас Бёвинг,

1
@TobiasBeuving Это сообщение об ошибке копирования вставили , потому что я NOT NULLустановить на id. Мне просто интересно, есть ли у вас предложения по этому делу. Это считается плохой практикой?
Марсель

84

Ответ Алекса требует некоторой осторожности (например, блокировки или транзакции) в мультиклиентских средах.

Предполагая, что AUTO IDполе является первым в таблице (обычный случай), мы можем использовать неявные транзакции.

    СОЗДАТЬ ВРЕМЕННУЮ ТАБЛИЦУ tmp SELECT * из счетов-фактур ГДЕ ...;
    ALTER TABLE tmp drop ID; # drop autoincrement field
    # UPDATE tmp SET ...; # просто нужно было изменить другие уникальные ключи
    INSERT INTO invoices SELECT 0, tmp. * FROM tmp;
    DROP TABLE tmp;

Из документов MySQL:

Использование AUTO_INCREMENT: вы также можете явно присвоить столбцу NULL или 0 для генерации порядковых номеров.


Это поместило для меня запись с идентификатором 0, хотя у меня есть поле AUTO_INCREMENT. Изменение его на null сработало, поэтому я предлагаю вам изменить код, чтобы использовать null вместо 0. Я использую MySQL 5.5.35 (Ubuntu).
Тайлер Кольер

@TylerCollier: NULLили 0оба являются приемлемыми. Можете ли вы опубликовать скриншоты проблемы, воспроизведя их, и поделиться ими здесь.
Рэвиндер Редди

Мой мозг болел, пытаясь понять аккуратный подзапрос в строке 4. Может быть, это объяснение помогает: таблица назначения invoicesсодержит IDполе AUTO_INCREMENT . Исходная таблица tmp- нет. Чтобы получить необходимую корреляцию 1: 1 во время вставки, вы указываете в 0качестве первого поля выбора, которое является полем AUTO_INCREMENT целевой таблицы. В tmp.*выбирает все поля из таблицы источника tmp. Отлично! Теперь у вас есть соотношение 1: 1. Замечательный ответ @Tim.
elbowlobstercowstand

@RavinderReddy NULL и 0 не одно и то же - возможно иметь идентификатор записи 0 - используйте NULL для такого рода вещей
Тобиас Бёвинг

@TobiasBeuving : Я прекрасно это понимаю, NULLи 0это не одно и то же. Текущий контекст находится в AUTO_INCREMENTфункции. Вы не можете вставить 0или NULLзначение в такие поля. Но при их использовании в качестве входных данных в такое поле, следующее значение последовательности будет присвоен этой области авто матически. Отсюда и мой комментарий. Доказательство на SQL Fiddle: sqlfiddle.com/#!9/d619f/1
Равиндер Редди

12

Вы точно ЗНАЕТЕ, что сработает DUPLICATE KEY, поэтому вы можете заранее выбрать MAX (ID) +1:

INSERT INTO invoices SELECT MAX(ID)+1, ... other fields ... FROM invoices AS iv WHERE iv.ID=XXXXX 

2
да, но я не хочу вводить все остальные поля (их более или менее 20, и со временем они могут измениться). Я не хочу менять код каждый раз при изменении структуры таблицы.
Цифры,

Вам придется. Лучше создать сценарий, который генерирует оператор SQL из списка полей. Это банально.
Инго

Единственная альтернатива - использовать триггер вставки (если mysql поддерживает это).
Инго

11

Ваш подход хорош, но проблема в том, что вы используете «*» вместо перечисления имен полей. Если вы поместите все имена столбцов, кроме первичного ключа, ваш скрипт будет работать как шарм на одной или нескольких записях.

INSERT INTO invoices (iv.field_name, iv.field_name,iv.field_name ) SELECT iv.field_name, iv.field_name,iv.field_name FROM invoices AS iv WHERE iv.ID=XXXXX


Однако это означает, что вам придется изменить код, как только поле будет добавлено в таблицу или удалено из нее, что я лично считаю ГЛАВНЫМ запретом. (Не всегда, но достаточно часто.)
Ремер

10

Я знаю поздний ответ, но это все еще распространенный вопрос, я хотел бы добавить еще один ответ, который сработал для меня, с использованием только одной строки insert into, и я думаю, что это просто, без создания какой-либо новой таблицы (поскольку он может быть проблемой с CREATE TEMPORARY TABLEразрешениями):

INSERT INTO invoices (col_1, col_2, col_3, ... etc)
  SELECT
    t.col_1,
    t.col_2,
    t.col_3,
    ...
    t.updated_date,
  FROM invoices t;

Решение работает для AUTO_INCREMENTстолбца id, в противном случае вы также можете добавить IDстолбец в оператор:

INSERT INTO invoices (ID, col_1, col_2, col_3, ... etc)
  SELECT
    MAX(ID)+1,
    t.col_1,
    t.col_2,
    t.col_3,
    ... etc ,
  FROM invoices t;

Это действительно просто и просто, вы можете обновить что-либо еще в одной строке без какого-либо второго оператора обновления на будущее (например, обновить столбец заголовка с дополнительным текстом или заменить строку другим), также вы можете указать, что именно вы хотите продублировать, если все, то есть, если некоторые, вы можете это сделать.


2
Это также хорошая основа для изменения некоторых полей при копировании. Я просто INSERT into wp_options SELECT NULL, 'theme_mods_twopress', option_value, autoload FROM wp_options where option_name = 'theme_mods_onepress';копировал при увеличении записи ( AUTO_INCREMENT NULLтрюк сверху) и option_nameполя.
Марсель Вальдфогель

Да .. Хорошая идея для меня. Спасибо!!
Рагу Натараджан

Лучшее решение
Эрлон Чарльз

3

Я просто хотел расширить отличный ответ Алекса, чтобы сделать его подходящим, если вы хотите продублировать весь набор записей:

SET @x=7;
CREATE TEMPORARY TABLE tmp SELECT * FROM invoices;
UPDATE tmp SET id=id+@x;
INSERT INTO invoices SELECT * FROM tmp;

Мне просто нужно было это сделать, и я нашел ответ Алекса идеальной отправной точкой! Конечно, вы должны установить @x на самый высокий номер строки в таблице (я уверен, что вы могли бы получить это с помощью запроса). Это полезно только в этой очень конкретной ситуации, поэтому будьте осторожны, используя его, если вы не хотите дублировать все строки. При необходимости скорректируйте математические расчеты.


2

У меня похожая проблема, и я делаю вот что:

insert into Preguntas  (`EncuestaID`, `Tipo` , `Seccion` , `RespuestaID` , `Texto` )  select '23', `Tipo`, `Seccion`, `RespuestaID`, `Texto` from Preguntas where `EncuestaID`= 18

Был Preguntas:

CREATE TABLE IF NOT EXISTS `Preguntas` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `EncuestaID` int(11) DEFAULT NULL,
  `Tipo` char(5) COLLATE utf8_unicode_ci DEFAULT NULL,
  `Seccion` int(11) DEFAULT NULL,
  `RespuestaID` bigint(11) DEFAULT NULL,
  `Texto` text COLLATE utf8_unicode_ci ,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=522 ;

Итак, IDавтоматически увеличивается, и я также использую фиксированное значение ('23') для EncuestaID.


извините, я только что понял, что вы не хотите использовать имя столбца, поэтому это не применимо
Alex Angelico

1

Мне это тоже было нужно; Мое решение заключалось в использовании SQLYOG (бесплатная версия) для экспорта желаемой записи как SQL (создает вставку).

Затем я вручную отредактировал это, чтобы удалить идентификатор, поскольку он должен быть автоматически сгенерирован, а затем скопировал вставку в SQLYog для ее выполнения. Это было безболезненно. Я думаю, что многие другие графические интерфейсы MySQL также могут это сделать.

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

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


0

Небольшая вариация, основное отличие состоит в том, чтобы установить для поля первичного ключа ("varname") значение null, которое выдает предупреждение, но работает. Установив для первичного ключа значение null, автоинкремент работает при вставке записи в последний оператор.

Этот код также очищает предыдущие попытки и может быть запущен более одного раза без проблем:

DELETE FROM `tbl` WHERE varname="primary key value for new record";
DROP TABLE tmp;
CREATE TEMPORARY TABLE tmp SELECT * FROM `tbl` WHERE varname="primary key value for old record";
UPDATE tmp SET varname=NULL;
INSERT INTO `tbl` SELECT * FROM tmp;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.