Дублювати / копіювати записи в тій самій таблиці 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рядок замість копіювання рядка. Хтось знає, як це виправити?

// редагування: Я хотів би зробити це, не вводячи всіх назв полів, оскільки назви полів можуть змінюватися з часом.

Відповіді:


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;

Сподіваюся, це добре працює для вас!


Мені подобається, один крок посередині, щоб стежити за речами
SeanDowney

4
Якщо ви копіюєте один запис, ви можете скинути де в оновлення та вставки. Потім ви можете просто натиснути двічі вгору в консолі mysql, що призведе до наступної останньої операції в історії, змінити ідентифікатор в оновленні, що зручно в кінці, натиснути Enter, натиснути, знову натиснути Enter, не змінюючи нічого, а потім повторити процедура кількох копій. Набагато швидше.
Крістіан Врабіє

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; # падіння поля автоінкременту
    # UPDATE tmp SET ...; # просто потрібно було змінити інші унікальні ключі
    ВСТАВИТИ У ФАКТУРИ SELECT 0, tmp. * FROM tmp;
    ТАБЛИЦЯ КРАПЛЕННЯ tmp;

З документів MySQL:

Використання AUTO_INCREMENT: Ви також можете явно призначити NULL або 0 стовпцю для генерації порядкових номерів.


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

@TylerCollier: NULLабо 0прийнятні обидва. Чи можете ви опублікувати знімки екрану випуску, відтворивши та поділившись тут.
Ravinder Reddy,

Мій мозок болить, намагаючись зрозуміти підзапит neato у рядку 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значення в такі поля. Але при їх використанні в якості вхідних даних в таке поле, таке значення послідовності буде присвоєно цій галузі авто матически. І звідси мій коментар. Доказ про скрипт
Reddy

12

Ви напевно ЗНАЄТЕ, що КЛЮЧ ДУБЛІКАТУ спрацює, таким чином, ви можете попередньо вибрати МАКС (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поле.
Марсель Вальдвогель,

Так .. Хороша ідея для мене. Дякую!!
Ragu Natarajan

Найкраще рішення
Ерлон Чарльз

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

Була прегунтас:

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.