Aynı MySQL tablosundaki kayıtları çoğaltın / kopyalayın


87

Bir süredir arıyordum ama sorunuma kolay bir çözüm bulamıyorum. Bir tablodaki bir kaydı kopyalamak istiyorum, ancak elbette benzersiz birincil anahtarın güncellenmesi gerekiyor.

Bu sorgum var:

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

sorun, bunun IDsatırı kopyalamak yerine yalnızca satırın değişmesidir . Bunun nasıl tamir edileceğini bilen biri var mı ?

// edit: Bunu tüm alan adlarını yazmadan yapmak istiyorum çünkü alan adları zamanla değişebilir.

Yanıtlar:


137

Genelde bunu yapma şeklim geçici bir masa kullanmaktır. Muhtemelen hesaplama açısından verimli değil ama iyi çalışıyor gibi görünüyor! Burada 99 kaydını bütünüyle kopyalıyorum, 100. kaydı oluşturuyorum.

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;

Umarım bu sizin için uygundur!



4
Tek bir kayıt kopyalıyorsanız, hem güncelleme hem de ekleme kısmındaki yeri bırakabilirsiniz. Daha sonra mysql konsolunda iki kez basabilirsiniz, bu da tarihteki son işlemden sonrakine kadar olanı getirir, güncellemede sonda uygun olan kimliği değiştirir, enter tuşuna basın, yukarı basın, hiçbir şeyi değiştirmeden tekrar enter tuşuna basın ve ardından tekrarlayın. birden çok kopya için prosedür. Çok daha hızlı.
Cristian Vrabie

4
ne durumda id 100 zaten oradaysa. İkincisi, yeni satırınız için herhangi bir yeni kimlik seçerseniz ve işlem sırasında başka biri yeni bir satır eklerse ve ardından kimliğiniz ve yeni eklenen kimlik aynı olacaktır. Bu, bu koddaki şişe boynu.
nbhatti2001

6
NULL kullanabilirsiniz:CREATE TEMPORARY TABLE tmp SELECT bla FROM invoices WHERE bla; UPDATE tmp SET id=NULL; INSERT INTO invoices SELECT * FROM tmp;
Tobias Beuving

1
@TobiasBeuving Bu kopyalayıp yapıştırılan bir hata mesajı çünkü ben NOT NULLayarladım id. Bu dava için bir öneriniz var mı merak ediyordum. Bu kötü bir uygulama olarak kabul edilir mi?
Marcel

84

Alex'in cevabı, çok müşterili ortamlarda biraz dikkat gerektiriyor (örn. Kilitleme veya bir işlem).

AUTO IDAlanın tablodaki ilk alan olduğunu varsayarsak (normal bir durum), örtük işlemleri kullanabiliriz.

    Faturalardan GEÇİCİ TABLO OLUŞTUR tmp SEÇİM * NEREDE ...;
    ALTER TABLE tmp bırakma kimliği; # drop autoincrement alanı
    # Tmp AYARINI GÜNCELLE ...; # sadece diğer benzersiz anahtarları değiştirmek için gerekli
    Faturalara INSERT INSERT INTO SELECT 0, tmp. * FROM tmp;
    DROP TABLE tmp;

MySQL belgelerinden:

AUTO_INCREMENT kullanarak: Sıra numaralarını oluşturmak için sütuna açık bir şekilde NULL veya 0 atayabilirsiniz.


Bu, bir AUTO_INCREMENT alanım olmasına rağmen kaydı benim için 0 kimliğiyle koydu. Null olarak değiştirmek işe yaradı, bu yüzden kodu 0 yerine null olarak değiştirmenizi öneririm. MySQL 5.5.35 (Ubuntu) kullanıyorum.
Tyler Collier

@TylerCollier: NULLveya 0her ikisi de kabul edilebilir. Sorunun ekran görüntülerini yeniden üreterek burada paylaşabilir misiniz?
Ravinder Reddy

4. satırdaki neato alt sorgusunu anlamaya çalışırken beynim ağrıyor. Belki bu açıklama yardımcı olur: Hedef tablo invoicesbir AUTO_INCREMENT IDalanı içerir . Kaynak tablo tmpdeğil. Ekleme sırasında gerekli 1: 1 korelasyonu elde etmek için 0, hedef tablonun AUTO_INCREMENT alanı olan seçimin ilk alanı olarak a belirtirsiniz . tmp.*Seçer kaynak tablodan tüm alanları tmp. Mükemmel! Artık 1: 1 korelasyonunuz var. Müthiş cevap @ Zaman.
elbowlobstercowstand

@RavinderReddy NULL ve 0 aynı değil - 0 kayıt kimliğine sahip olmak mümkündür - bu tür şeyler için NULL kullanın
Tobias Beuving

@TobiasBeuving : Ben farkındayım NULLve 0aynı değildir. Mevcut bağlam AUTO_INCREMENTözellik üzerindedir . Bu tür alanlara bir 0veya NULLdeğer ekleyemezsiniz . Ancak bunları böyle bir alana girdi olarak kullandığınızda, sonraki sıra değeri o alana otomatik olarak atanacaktır . Ve dolayısıyla benim yorumum. SQL Fiddle'ın
Ravinder Reddy

12

DUPLICATE ANAHTARININ tetikleneceğini kesin olarak BİLİYORSUNUZ, böylece önceden MAX (ID) +1 seçebilirsiniz:

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

2
evet, ancak diğer tüm alanları yazmak istemiyorum (az ya da çok 20 var ve zamanla değişebilir). Tablo her değiştiğinde kodu değiştirmek istemiyorum.
rakamları

Zorunda olacaksın. Bir alan listesinden SQL ifadesini oluşturan bir komut dosyası oluştursanız iyi olur. Önemsiz.
Ingo

Diğer tek alternatif, ekleme tetikleyicisi kullanmaktır (mysql bunu destekliyorsa).
Ingo

11

Yaklaşımınız iyidir, ancak sorun, alan adlarını listelemek yerine "*" kullanmanızdır. Birincil anahtar hariç tüm sütun adlarını koyarsanız, komut dosyanız bir veya daha fazla kayıtta charm gibi çalışacaktır.

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


Ancak bu, kişisel olarak MAJOR no-no bulduğum tabloya bir alan eklendiğinde veya tablodan çıkarıldığında kodu değiştirmeniz gerekeceği anlamına gelir. (Her zaman değil, ama çoğu zaman yeterli.)
Roemer

10

Geç cevap biliyorum, ama yine de yaygın bir soru, benim için işe yarayan başka bir cevabı eklemek istiyorum, sadece tek bir satır insert intoifadesi kullanarak ve yeni bir tablo oluşturmadan basit olduğunu düşünüyorum (çünkü CREATE TEMPORARY TABLEizinlerle ilgili bir sorun olabilir ):

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;

Çözüm AUTO_INCREMENTid sütunu için çalışıyor , aksi takdirde IDifadeye sütun da ekleyebilirsiniz :

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;

Gerçekten kolay ve anlaşılırdır, tek bir satırdaki diğer her şeyi daha sonra herhangi bir ikinci güncelleme ifadesi olmadan güncelleyebilirsiniz (örn: bir başlık sütununu fazladan metinle güncelleme veya bir dizeyi başka bir diziyle değiştirme), ayrıca tam olarak çoğaltmak istiyorsanız, eğer öyleyse, eğer bazılarıysa, bunu yapabilirsiniz.


2
Bu aynı zamanda kopyalama sırasında bazı alanları değiştirmek için iyi bir temeldir. Sadece INSERT into wp_options SELECT NULL, 'theme_mods_twopress', option_value, autoload FROM wp_options where option_name = 'theme_mods_onepress';girişi ( AUTO_INCREMENT NULLyukarıdan aldatmaca) ve option_namealanı arttırırken kopyalıyordum .
Marcel Waldvogel

Evet .. Benim için iyi fikir. Teşekkür ederim!!
Ragu Natarajan

En iyi çözüm
Erlon Charles

3

Alex'in harika cevabını, kayıt kümesinin tamamını kopyalamak isteyip istemediğinizi uygun hale getirmek için genişletmek istedim:

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

Bunu yapmak zorundaydım ve Alex'in cevabını mükemmel bir başlangıç ​​noktası buldum !. Elbette, @x'i tablodaki en yüksek satır numarasına ayarlamanız gerekir (eminim bunu bir sorgu ile yakalayabilirsiniz). Bu yalnızca bu çok özel durumda yararlıdır, bu nedenle tüm satırları kopyalamak istemediğinizde kullanırken dikkatli olun. Matematiği gerektiği gibi ayarlayın.


2

Benzer bir sorunum var ve bunu yapıyorum:

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

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

Yani, IDotomatik olarak artırılır ve ayrıca için sabit bir değer ('23') kullanıyorum EncuestaID.


üzgünüm, sütun adını kullanmak istemediğinizi fark ettim, bu yüzden bu geçerli değil
Alex Angelico

1

Buna da ihtiyacım vardı; benim çözümüm, istenen kaydı SQL olarak dışa aktarmak için SQLYOG'u (ücretsiz sürüm) kullanmaktı (bir ekleme oluşturur).

Daha sonra, otomatik olarak oluşturulması gerektiğinden kimliği kaldırmak için bunu elle düzenledim ve daha sonra eklemek için SQLYog'a kopyaladım. Bu acısızdı. Sanırım diğer birçok MySQL GUI de bunu yapabilir.

Bu bana canlı bir sistemde test amaçlı kullanabileceğim bir kayıt sağlıyor.

Tablo her gün yeniden yazıldığından, artık bu eki de yeniden kullanmak için aldım.


0

Hafif varyasyon, temel fark birincil anahtar alanını ("değişken adı") null olarak ayarlamaktır, bu bir uyarı üretir ancak çalışır. Birincil anahtarı null olarak ayarlayarak, otomatik artış, kaydı son ifadeye eklerken çalışır.

Bu kod aynı zamanda önceki girişimleri de temizler ve birden çok kez sorunsuz bir şekilde çalıştırılabilir:

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;

0

NOT NULL gereksinimi olmadan kimlik alanını bigint olarak değiştirmek için geçici tabloyu değiştirebilir, ardından bu geçici tabloda kimliği 0 olarak ayarlayabilirsiniz. Bundan sonra orijinal tabloya geri ekleyin ve NULL otomatik artışı tetikleyecektir.

CREATE TEMPORARY TABLE tmptable SELECT * FROM x WHERE (id='123');
ALTER TABLE tmptable CHANGE id id bigint;
UPDATE tmptable SET id = NULL;
INSERT INTO x SELECT * FROM tmptable;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.