Gandakan / Salin catatan di tabel MySQL yang sama

87

Saya telah mencari beberapa waktu sekarang tetapi saya tidak dapat menemukan solusi yang mudah untuk masalah saya. Saya ingin menduplikasi catatan dalam tabel, tetapi tentu saja, kunci utama yang unik perlu diperbarui.

Saya punya pertanyaan ini:

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

Masalahnya adalah ini hanya mengubah IDbaris, bukan menyalin baris. Apakah ada yang tahu cara memperbaikinya?

// edit: Saya ingin melakukan ini tanpa mengetik semua nama field karena nama field dapat berubah seiring waktu.

Digit
sumber

Jawaban:

137

Cara yang biasanya saya lakukan adalah menggunakan tabel sementara. Ini mungkin tidak efisien secara komputasi tetapi tampaknya berfungsi dengan baik! Di sini saya menduplikasi record 99 secara keseluruhan, membuat record 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;

Semoga berhasil untuk Anda!

Alex
sumber
Saya suka, satu langkah di tengah untuk mengawasi berbagai hal
SeanDowney
4
Jika Anda menyalin satu catatan, Anda dapat membuang tempat di pembaruan dan penyisipan. Kemudian Anda bisa menekan dua kali di konsol mysql yang akan memunculkan operasi terakhir berikutnya dalam sejarah, mengubah id dalam pembaruan yang nyaman di bagian akhir, tekan enter, tekan ke atas, tekan enter lagi tanpa mengubah apa pun, lalu ulangi prosedur untuk beberapa salinan. Lebih cepat.
Cristian Vrabie
4
bagaimana jika id 100 sudah ada. Secondlty jika Anda memilih id baru untuk baris baru Anda, dan selama operasi orang lain memasukkan baris baru dan kemudian id Anda dan id yang baru dimasukkan akan sama. Ini adalah leher botol dalam kode ini.
nbhatti2001
6
Anda dapat menggunakan NULL: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 Itu adalah pesan kesalahan salin-tempel karena saya telah NOT NULLmenyetelnya id. Saya hanya ingin tahu apakah Anda punya saran untuk kasus ini. Apakah itu dianggap praktik yang buruk?
Marcel
84

Jawaban Alex membutuhkan perhatian (misalnya penguncian atau transaksi) di lingkungan multi-klien.

Dengan asumsi AUTO IDbidang tersebut adalah yang pertama dalam tabel (kasus biasa), kita dapat menggunakan transaksi implisit.

    BUAT TABEL TEMPORER tmp SELECT * dari faktur DIMANA ...;
    ALTER TABLE tmp drop ID; # jatuhkan bidang autoincrement
    # PERBARUI SET tmp ...; # hanya perlu mengubah kunci unik lainnya
    MASUKKAN KE faktur PILIH 0, tmp. * DARI tmp;
    DROP TABLE tmp;

Dari dokumen MySQL:

Menggunakan AUTO_INCREMENT: Anda juga dapat secara eksplisit menetapkan NULL atau 0 ke kolom untuk menghasilkan nomor urut.

Tim Ruehsen
sumber
Ini memasukkan catatan dengan ID 0 untuk saya, meskipun saya memiliki bidang AUTO_INCREMENT. Mengubahnya menjadi null berfungsi, jadi saya sarankan Anda mengubah kode untuk menggunakan null, bukan 0. Saya menggunakan MySQL 5.5.35 (Ubuntu).
Tyler Collier
@TylerCollier: NULLatau 0keduanya dapat diterima. Dapatkah Anda memposting tangkapan layar masalah dengan mereproduksi dan membagikannya di sini.
Ravinder Reddy
Otak saya sakit saat mencoba memahami subkueri neato di baris 4. Mungkin penjelasan ini membantu: Tabel tujuan invoicesberisi IDbidang AUTO_INCREMENT . Tabel sumber tmptidak. Untuk mendapatkan korelasi 1: 1 yang diperlukan selama penyisipan, Anda menentukan a 0sebagai bidang pertama dari pilih yang merupakan bidang AUTO_INCREMENT tabel tujuan. The tmp.*menyeleksi semua bidang dari tabel sumber tmp. Sempurna! Anda sekarang memiliki korelasi 1: 1. Jawaban luar biasa @Tim.
elbowlobstercowstand
@RavinderReddy NULL dan 0 tidak sama - dimungkinkan untuk memiliki record-id 0 - gunakan NULL untuk hal semacam ini
Tobias Beuving
@TobiasBeuving : Saya sangat menyadari hal itu NULLdan 0tidak sama. Konteks saat ini ada di AUTO_INCREMENTfitur. Anda tidak dapat menyisipkan 0atau NULLnilai ke dalam bidang seperti itu. Tapi ketika Anda menggunakannya sebagai masukan ke lapangan seperti itu, nilai urutan berikutnya akan ditugaskan dengan bidang auto matically. Dan karena itulah komentar saya. Bukti tentang SQL Fiddle: sqlfiddle.com/#!9/d619f/1
Ravinder Reddy
12

Anda TAHU pasti, bahwa DUPLICATE KEY akan terpicu, sehingga Anda dapat memilih MAX (ID) +1 terlebih dahulu:

INSERT INTO invoices SELECT MAX(ID)+1, ... other fields ... FROM invoices AS iv WHERE iv.ID=XXXXX 
Ingo
sumber
2
ya, tetapi saya tidak ingin mengetik semua bidang lainnya (ada lebih atau kurang 20, dan mereka mungkin berubah seiring waktu). Saya tidak ingin mengubah kode setiap kali perubahan struktur tab.
Digit
Kamu harus. Sebaiknya Anda membuat skrip yang menghasilkan pernyataan SQL dari daftar bidang. Itu sepele.
Ingo
Satu-satunya alternatif lain adalah dengan menggunakan pemicu sisipan (jika mysql mendukungnya).
Ingo
11

Pendekatan Anda bagus tetapi masalahnya adalah Anda menggunakan "*" alih-alih meminta nama bidang. Jika Anda meletakkan semua nama kolom kecuali kunci utama, skrip Anda akan berfungsi seperti pesona pada satu atau banyak rekaman.

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

Kotak Sempurna
sumber
Namun itu berarti Anda harus mengubah kode segera setelah bidang ditambahkan ke atau dijatuhkan dari tabel, yang secara pribadi saya temukan sebagai MAJOR no-no. (Tidak selalu, tapi cukup sering.)
Roemer
10

Jawaban terlambat yang saya tahu, tetapi ini masih merupakan pertanyaan umum, saya ingin menambahkan jawaban lain yang berhasil untuk saya, dengan hanya menggunakan satu baris insert intopernyataan, dan menurut saya ini sangat mudah, tanpa membuat tabel baru (karena dapat menjadi masalah dengan CREATE TEMPORARY TABLEizin):

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;

Solusinya bekerja untuk AUTO_INCREMENTkolom id, jika tidak, Anda dapat menambahkan IDkolom juga ke pernyataan:

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;

Ini sangat mudah dan lugas, Anda dapat memperbarui apa pun dalam satu baris tanpa pernyataan pembaruan kedua untuk nanti, (mis: memperbarui kolom judul dengan teks tambahan atau mengganti string dengan yang lain), juga Anda dapat lebih spesifik dengan apa sebenarnya Anda ingin menduplikasi, jika semuanya, jika beberapa, Anda dapat melakukannya.

Al-Mothafar
sumber
2
Ini juga merupakan dasar yang baik untuk memodifikasi beberapa bidang saat menyalin. Saya hanya biasa INSERT into wp_options SELECT NULL, 'theme_mods_twopress', option_value, autoload FROM wp_options where option_name = 'theme_mods_onepress';menyalin sambil meningkatkan entri ( AUTO_INCREMENT NULLtrik dari atas) dan option_namebidang juga.
Marcel Waldvogel
Ya .. Ide bagus untuk saya. Terima kasih!!
Ragu Natarajan
Solusi terbaik
Erlon Charles
3

Saya hanya ingin menyampaikan jawaban hebat Alex agar sesuai jika Anda kebetulan ingin menduplikasi seluruh rangkaian catatan:

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

Saya hanya harus melakukan ini dan menemukan jawaban Alex sebagai titik awal yang sempurna !. Tentu saja, Anda harus menyetel @x ke nomor baris tertinggi dalam tabel (saya yakin Anda bisa mengambilnya dengan kueri). Ini hanya berguna dalam situasi yang sangat spesifik ini, jadi berhati-hatilah menggunakannya saat Anda tidak ingin menduplikasi semua baris. Sesuaikan matematika seperlunya.

bcsteeve
sumber
2

Saya memiliki masalah serupa, dan inilah yang saya lakukan:

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 ;

Jadi, IDsecara otomatis bertambah dan saya juga menggunakan nilai tetap ('23') untuk EncuestaID.

Alex Angelico
sumber
maaf, saya baru menyadari Anda tidak ingin menggunakan nama kolom, jadi ini tidak berlaku
Alex Angelico
1

Saya membutuhkan ini juga; solusi saya adalah menggunakan SQLYOG (versi gratis) untuk mengekspor catatan yang diinginkan sebagai SQL (membuat sisipan).

Saya kemudian mengedit tangan ini untuk menghapus id karena ini perlu dibuat secara otomatis dan kemudian menyalin sisipan ke SQLYog untuk menjalankannya. Ini tidak menyakitkan. Saya kira banyak GUI MySQL lain yang dapat melakukan ini juga.

Ini memberi saya catatan yang dapat saya gunakan untuk tujuan pengujian pada sistem langsung.

Sekarang saya memiliki sisipan ini untuk digunakan kembali juga, karena tabel ditulis ulang setiap hari.

zzapper
sumber
0

Sedikit variasi, perbedaan utamanya adalah menyetel bidang kunci utama ("varname") ke null, yang menghasilkan peringatan tetapi berfungsi. Dengan menyetel kunci utama ke null, penambahan otomatis berfungsi saat memasukkan record di pernyataan terakhir.

Kode ini juga membersihkan upaya sebelumnya, dan dapat dijalankan lebih dari sekali tanpa masalah:

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;
Nik Dow
sumber
0

Anda dapat mengubah tabel sementara untuk mengubah bidang ID menjadi bigint atau lebih tanpa persyaratan NOT NULL, kemudian mengatur ID ke 0 dalam tabel temp itu. Setelah itu tambahkan kembali ke tabel asli dan NULL akan memicu kenaikan otomatis.

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;
Desiree
sumber