permintaan tentang menggabungkan pembaruan dan permintaan memasukkan ke dalam satu permintaan di mysql

9

saya ingin melacak riwayat perubahan untuk pengguna, sehingga setiap kali dia mengubah profilnya, saya perlu mengambil data lama dan menyimpannya di riwayat dan memperbarui dengan data baru.

Saya bisa menggunakan a selectuntuk mendapatkan data lama, insertsejarah, dan akhirnya updatemengubah data.

dapatkah saya memiliki semua ini dalam satu permintaan di mysql tanpa menggunakan prosedur tersimpan, pemicu, dll. seperti menggunakan kunci dll. jika demikian beri saya contoh kecil.

Saravanan
sumber
1
@savaranan: Pertanyaan ini layak diberi +1 karena ia memberikan pengingat yang kuat kepada DBA dan Pengembang untuk menggunakan transaksi dan memanfaatkan sepenuhnya properti ACID dari database.
RolandoMySQLDBA
2
@savaranan: Untuk semua maksud dan tujuan, Jack memberikan jawaban HANYA yang masuk akal. Bahkan, Jack Douglas mengambil langkah tambahan dan memaksakan kunci intermiten pada setiap baris dengan id = 10 untuk perlindungan MVCC tambahan dengan melakukan SELECT ... FOR UPDATE. Jawabannya lebih lanjut menekankan poin yang Jack dan saya telah katakan selama ini: PEMBARUAN dan INSERT tidak dapat, atau pernah menjadi, permintaan tunggal, mereka hanya bisa menjadi transaksi tunggal untuk perilaku SQL yang diajukan pertanyaan Anda.
RolandoMySQLDBA

Jawaban:

13

Untuk melakukan hal ini tanpa risiko memblokir pengguna lain mencoba untuk memperbarui profil yang sama pada saat yang sama, Anda perlu untuk mengunci baris di t1pertama, kemudian gunakan transaksi (sebagai Rolando menunjukkan di komentar untuk pertanyaan Anda):

start transaction;
select id from t1 where id=10 for update;
insert into t2 select * from t1 where id=10;
update t1 set id = 11 where id=10;
commit;
Jack mengatakan coba topanswers.xyz
sumber
Ini sangat brilian dalam mengunci lebih lanjut setiap baris dengan id = 10. Itu seharusnya +2. Yang bisa saya berikan adalah +1 !!!
RolandoMySQLDBA
1

Saya tidak percaya ada cara untuk menggabungkan ketiga pernyataan itu. Hal terdekat dengan itu tidak benar-benar membantu Anda, dan itu adalah SET SELECT. Taruhan terbaik Anda adalah pemicu. Di bawah ini adalah contoh pemicu yang sering saya gunakan untuk mempertahankan jejak audit (dibangun dengan PHP):

$trigger = "-- audit trigger --\nDELIMITER $ \n".
    "DROP TRIGGER IF EXISTS `{$prefix}_Audit_Trigger`$\n".
    "CREATE TRIGGER `{$prefix}_Audit_Trigger` AFTER UPDATE ON `$this->_table_name` FOR EACH ROW BEGIN\n";

foreach ($field_defs as $field_name => $field) {
    if ($field_name != $id_name) {
       $trigger .= "IF (NOT OLD.$field_name <=> NEW.$field_name) THEN \n".'INSERT INTO AUDIT_LOG ('.
                    'Table_Name, Row_ID, Field_Name, Old_Value, New_Value, modified_by, DB_User) VALUES'.
                    "\n ('$this->_table_name',OLD.$this->_id_name,'$field_name',OLD.$field_name,NEW.$field_name,".
                    "NEW.modified_by, USER()); END IF;\n";
    }
}
$trigger .= 'END$'."\n".'DELIMITER ;';
Bryan Agee
sumber
-3

Saya telah menemukan bahwa kueri ini berfungsi di SQL & MySQL Server INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Semoga ini bermanfaat bagi orang lain juga di masa depan.

Saravanan
sumber
4
Ini bukan pertanyaan. Ini sebenarnya dua permintaan yang harus diperlakukan sebagai transaksi.
RolandoMySQLDBA
@rolandomysqldba: ini berfungsi dengan baik sebagai permintaan tunggal ketika saya mengirim ke server db dari kode aplikasi, di mana saya memperlakukan set ini sebagai permintaan tunggal. Mengapa engkau berkata begitu?. dapatkah kau menyangkal ini dengan alasan kuat ..
Saravanan
2
@saravanan: Di mata InnoDB atau RDBMS yang sesuai dengan ACID (Oracle, SQLServer, PostreSQL, Sybase, dll.), tidak mungkin untuk memanggil kedua pernyataan SQL itu satu kueri. Sebagai basis data ACID-compliant akan memperlakukan mereka sebagai dua pernyataan. Secara default, InnoDB telah otomatis dinonaktifkan. Pernyataan pertama, INSERT, akan dieksekusi sebagai transaksi tunggal. Data Multiversioning Concurrency Control (MVCC) akan dihasilkan untuk menyimpan salinan data asli dalam tabel t2 pada basis baris demi baris. Jika MySQL lumpuh saat menjalankan INSERT, InnoDB menggunakan data MVCC untuk mengembalikan t2 ke kondisi semula.
RolandoMySQLDBA
1
@saravanan: Misalkan INSERT berhasil berfungsi. Data yang dihasilkan dari INSERT telah dilakukan (dengan autocommit ON) dan tabel melindungi MVCC t2 dibuang. Ketika Anda melakukan UPDATE, MVCC dihasilkan terhadap tabel t1 dan UPDATE dilakukan. Jika MySQL lumpuh saat UPDATE, InnoDB menggunakan data MVCC pada t1 untuk mengembalikan UPDATE. Bahkan jika UPDATE mengubah hanya satu baris, kemungkinan satu-dalam-miloion ada dari memindahkan catatan dari t1 ke t2 dengan id 10 dan tidak mengubah id 10 ke id 11 di t1. Untuk mencegah skenario unik ini, Anda perlu melakukan yang berikut ...
RolandoMySQLDBA
@savaranan: Perlakukan dua pernyataan SQL sebagai satu transaksi. Cara sederhana untuk melakukan ini adalah: BEGIN; Masukkan ke T2 PILIH * DARI t1 DI MANA id = 10; PEMBARUAN t1 SET id = 11 WHERE id = 10; MELAKUKAN; Alasan terkuat untuk memperlakukan dua pernyataan SQL sebagai satu transaksi adalah fakta bahwa MVCC yang dibuat untuk INSERT akan tetap ada selama UPDATE. Jika kerusakan MySQL harus terjadi selama UPDATE di dalam transaksi (BEGIN; ... COMMIT; block) MVCC akan mengembalikan semua perubahan ke kondisi yang konsisten. Jika kedua INSERT dan UPDATE selesai, maka MVCC dibuang pada saat terakhir.
RolandoMySQLDBA