Saya perlu melakukan UPSERT / INSERT ATAU UPDATE terhadap Database SQLite.
Ada perintah INSERT OR REPLACE yang dalam banyak kasus dapat berguna. Tetapi jika Anda ingin mempertahankan id Anda dengan autoincrement di tempatnya karena kunci asing, itu tidak berfungsi karena menghapus baris, membuat yang baru dan akibatnya baris baru ini memiliki ID baru.
Ini akan menjadi tabelnya:
pemain - (kunci utama pada id, nama_pengguna unik)
| id | user_name | age |
------------------------------
| 1982 | johnny | 23 |
| 1983 | steven | 29 |
| 1984 | pepee | 40 |
db.execSQL("insert into bla(id,name) values (?,?) on conflict(id) do update set name=?")
. Memberi saya kesalahan sintaks pada kata "on"Gaya Tanya Jawab
Nah, setelah meneliti dan berjuang dengan masalah selama berjam-jam, saya menemukan bahwa ada dua cara untuk melakukannya, tergantung pada struktur tabel Anda dan jika Anda mengaktifkan pembatasan kunci asing untuk menjaga integritas. Saya ingin membagikan ini dalam format yang bersih untuk menghemat waktu kepada orang-orang yang mungkin berada dalam situasi saya.
Opsi 1: Anda mampu menghapus baris tersebut
Dengan kata lain, Anda tidak memiliki kunci asing, atau jika Anda memilikinya, mesin SQLite Anda dikonfigurasi sehingga tidak ada pengecualian integritas. Cara untuk pergi adalah INSERT OR REPLACE . Jika Anda mencoba memasukkan / memperbarui pemain yang ID-nya sudah ada, mesin SQLite akan menghapus baris itu dan memasukkan data yang Anda berikan. Sekarang pertanyaannya muncul: apa yang harus dilakukan agar ID lama tetap terkait?
Katakanlah kita ingin UPSERT dengan data user_name = 'steven' dan age = 32.
Lihat kode ini:
Triknya sedang menyatu. Ia mengembalikan id pengguna 'steven' jika ada, dan sebaliknya, ia mengembalikan id baru yang baru.
Opsi 2: Anda tidak mampu menghapus baris tersebut
Setelah berkutat dengan solusi sebelumnya, saya menyadari bahwa dalam kasus saya itu bisa menghancurkan data, karena ID ini berfungsi sebagai kunci asing untuk tabel lain. Selain itu, saya membuat tabel dengan klausul ON DELETE CASCADE , yang berarti akan menghapus data secara diam-diam. Berbahaya.
Jadi, saya pertama kali memikirkan klausa IF, tetapi SQLite hanya memiliki CASE . Dan KASUS ini tidak dapat digunakan (atau setidaknya saya tidak mengelolanya) untuk melakukan satu kueri UPDATE jika SUDAH (pilih id dari pemain di mana user_name = 'steven'), dan MASUKKAN jika tidak. Tidak pergi.
Dan kemudian, akhirnya saya menggunakan kekerasan, dengan sukses. Logikanya adalah, untuk setiap UPSERT yang ingin Anda lakukan, pertama-tama lakukan INSERT ATAU IGNORE untuk memastikan ada baris dengan pengguna kami, lalu jalankan kueri UPDATE dengan data yang sama persis dengan yang Anda coba masukkan.
Data yang sama seperti sebelumnya: user_name = 'steven' dan age = 32.
Dan itu saja!
EDIT
Seperti yang telah dikomentari Andy, mencoba memasukkan terlebih dahulu dan kemudian memperbarui dapat menyebabkan pemicu pengaktifan lebih sering dari yang diharapkan. Menurut saya, ini bukan masalah keamanan data, tetapi memang benar bahwa mengaktifkan peristiwa yang tidak perlu tidak masuk akal. Oleh karena itu, solusi yang lebih baik adalah:
sumber
Berikut adalah pendekatan yang tidak memerlukan 'abaikan' kekerasan yang hanya akan berfungsi jika ada pelanggaran kunci. Cara ini bekerja berdasarkan kondisi apa pun yang Anda tentukan di pembaruan.
Coba ini...
Bagaimana itu bekerja
'Saus ajaib' di sini digunakan
Changes()
dalamWhere
klausa.Changes()
mewakili jumlah baris yang dipengaruhi oleh operasi terakhir, yang dalam hal ini adalah pembaruan.Dalam contoh di atas, jika tidak ada perubahan dari pembaruan (misalnya catatan tidak ada) maka
Changes()
= 0 sehinggaWhere
klausa dalamInsert
pernyataan mengevaluasi menjadi benar dan baris baru dimasukkan dengan data yang ditentukan.Jika
Update
memang memperbarui baris yang ada, makaChanges()
= 1 (atau lebih akurat, bukan nol jika lebih dari satu baris diperbarui), sehingga klausa 'Di mana' dalamInsert
sekarang mengevaluasi ke salah dan dengan demikian tidak ada penyisipan yang akan dilakukan.Keindahan dari hal ini adalah tidak diperlukan kekerasan fisik, atau penghapusan yang tidak perlu, lalu memasukkan kembali data yang dapat mengakibatkan mengacaukan kunci hilir dalam hubungan kunci asing.
Selain itu, karena ini hanya
Where
klausa standar , dapat didasarkan pada apa pun yang Anda tentukan, bukan hanya pelanggaran kunci. Demikian juga, Anda dapat menggunakanChanges()
kombinasi dengan apa pun yang Anda inginkan / butuhkan di mana pun ekspresi diizinkan.sumber
Changes() = 0
akan mengembalikan false dan dua baris akan melakukan INSERT OR REPLACEUPSERT
? Namun demikian, ada baiknya pembaruan terjadi, pengaturanChanges=1
atauINSERT
pernyataan tersebut akan salah aktif, yang tidak Anda inginkan.Masalah dengan semua jawaban yang disajikan itu sama sekali tidak memperhitungkan pemicu (dan mungkin efek samping lainnya). Solusi seperti
mengarah ke kedua pemicu dieksekusi (untuk menyisipkan dan kemudian untuk pembaruan) ketika baris tidak ada.
Solusi yang tepat adalah
dalam hal ini hanya satu pernyataan yang dieksekusi (ketika ada baris atau tidak).
sumber
UPDATE OR IGNORE
perlu, karena pembaruan tidak akan macet jika tidak ada baris yang ditemukan.Untuk memiliki UPSERT murni tanpa lubang (untuk pemrogram) yang tidak menyampaikan kunci unik dan lainnya:
SELECT perubahan () akan mengembalikan jumlah pembaruan yang dilakukan dalam penyelidikan terakhir. Kemudian periksa apakah nilai kembalian dari perubahan () adalah 0, jika demikian jalankan:
sumber
Anda juga dapat menambahkan klausa ON CONFLICT REPLACE ke batasan unik user_name Anda dan kemudian cukup SISIPKAN, serahkan pada SQLite untuk mencari tahu apa yang harus dilakukan jika terjadi konflik. Lihat: https://sqlite.org/lang_conflict.html .
Perhatikan juga kalimat terkait pemicu hapus: Saat strategi penyelesaian konflik REPLACE menghapus baris untuk memenuhi batasan, hapus pemicu yang diaktifkan jika dan hanya jika pemicu rekursif diaktifkan.
sumber
Opsi 1: Sisipkan -> Perbarui
Jika Anda ingin menghindari keduanya
changes()=0
danINSERT OR IGNORE
bahkan jika Anda tidak mampu menghapus baris - Anda dapat menggunakan logika ini;Pertama, masukkan (jika tidak ada) lalu perbarui dengan memfilter dengan kunci unik.
Contoh
Mengenai Pemicu
Perhatian: Saya belum mengujinya untuk melihat pemicu mana yang dipanggil, tetapi saya menganggap yang berikut:
jika baris tidak ada
jika baris memang ada
Opsi 2: Sisipkan atau ganti - simpan ID Anda sendiri
dengan cara ini Anda dapat memiliki satu perintah SQL
Edit: opsi tambahan 2.
sumber