SQLite INSERT - ON DUPLICATE KEY UPDATE (UPSERT)

98

MySQL memiliki sesuatu seperti ini:

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON DUPLICATE KEY UPDATE hits = hits + 1;

Sejauh yang saya tahu fitur ini tidak ada di SQLite, yang ingin saya ketahui adalah apakah ada cara untuk mencapai efek yang sama tanpa harus menjalankan dua kueri. Selain itu, jika tidak memungkinkan, apa yang Anda sukai:

  1. PILIH + (INSERT atau UPDATE) atau
  2. UPDATE (+ INSERT jika UPDATE gagal )
Alix Axel
sumber

Jawaban:

31

Karena 3.24.0 SQLite juga mendukung upsert , jadi sekarang Anda cukup menulis yang berikut ini

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON CONFLICT(ip) DO UPDATE SET hits = hits + 1;
szmate1618
sumber
3
Saya bertanya-tanya apakah Anda dapat melakukan banyak upserthal seperti ini dalam satu transaksi, yaitu dengan executemany()fungsi Python ?
Radio Controlled
117
INSERT OR IGNORE INTO visits VALUES ($ip, 0);
UPDATE visits SET hits = hits + 1 WHERE ip LIKE $ip;

Ini membutuhkan kolom "ip" untuk memiliki batasan UNIK (atau KUNCI UTAMA).


EDIT: Solusi hebat lainnya: https://stackoverflow.com/a/4330694/89771 .

dan04
sumber
2
Sekadar catatan, REPLACEbukanlah suatu pilihan.
Alix Axel
1
Mengenai tautan "solusi hebat lainnya", saya juga akan mempertimbangkan jawaban berbeda untuk pertanyaan yang sama: stackoverflow.com/a/418988/3650835
KayakinKoder
19

Saya lebih suka UPDATE (+ INSERT if UPDATE fails). Lebih sedikit kode = lebih sedikit bug.

codeholic
sumber
1
Terima kasih! @Sam ( stackoverflow.com/questions/418898/… ) sepertinya setuju dengan Anda. Saya juga lebih suka pendekatan ini.
Alix Axel
@Smith Maksud saya menggunakan pernyataan UPDATE dan INSERT polos dan memeriksa nilai kembali.
Codeholic
Ini tidak memiliki atomicity dimungkinkan INSERT akan gagal jika beberapa proses lain dimasukkan di antaranya.
Robin Lavallée
7

Jawaban saat ini hanya akan berfungsi di sqlite ATAU mysql (tergantung apakah Anda menggunakan ATAU atau tidak). Jadi, jika Anda menginginkan kompatibilitas cross dbms, berikut ini yang akan dilakukan ...

REPLACE INTO `visits` (ip, value) VALUES ($ip, 0);
Jacob Thomason
sumber
3
Jawaban yang diterima berfungsi di SQLite (itulah tujuan saya). REPLACEakan bekerja di SQLite juga, tetapi di MySQL itu akan selalu mengatur ulang penghitung ke 0 - sementara kueri akan portabel, hasil akhirnya akan sangat berbeda.
Alix Axel
Anda benar, saya pikir OP sedang mencari sesuatu yang portabel. Saya menyadari REPLACE INTO tidak akan berfungsi dengan semua kasus, terutama jika pengawetan PK diperlukan, tetapi akan berhasil untuk banyak kasus.
Jacob Thomason
Gagal dengan rapi alih-alih membuang data adalah fitur, bukan bug.
Tobu
-4

Anda harus menggunakan memcache untuk ini karena ini adalah kunci tunggal (alamat IP) yang menyimpan satu nilai (jumlah kunjungan). Anda dapat menggunakan fungsi kenaikan atom untuk memastikan tidak ada kondisi "perlombaan".

Ini lebih cepat dari MySQL dan menghemat beban sehingga MySQL dapat fokus pada hal lain.

Xeoncross
sumber
Jika datanya tidak terlalu penting, ya. Namun, jika ini digunakan di situs sibuk di mana banyak IP masuk ke layanan, instance memcache mungkin penuh dan menyebabkan beberapa konten terhapus. Mencadangkan konten memcache juga akan menarik (jika diperlukan.)
Elliot Foster
@ElliotFoster Memcached dapat menangani data sebanyak RAM yang Anda lemparkan (jika Anda ingin ketekunan, gunakan juga redis atau membase). Jika Anda mendapatkan lebih dari 1 juta pengunjung setiap hari maka Anda mungkin mampu memberikan contoh memcache Anda lebih dari 30MB ram (yang menurut saya default). Namun, itu pasti dapat menangani beban yang jauh lebih tinggi daripada SQLite dan MySQL untuk jumlah memori yang Anda berikan - tidak ada perbandingan.
Xeoncross
Mohon jangan salahkan komentar saya sebagai pemungutan suara terhadap memcache, karena menurut saya ini adalah alat yang luar biasa. Seperti redis (saya tidak dapat berbicara untuk membase, karena saya belum menggunakannya.) Namun, memcache / redis bukanlah toko yang paling dapat diandalkan. Ya, redis memiliki ketekunan, tetapi data disimpan ke disk pada interval (terakhir saya melihat) dan tidak memcache sama sekali. Seperti yang saya katakan, jika datanya tidak penting (atau dapat direproduksi dengan mudah) maka memcache dan perusahaan itu hebat. Posting asli juga menanyakan tentang sqlite, yang sangat berbeda dari MySQL dan kemungkinan berarti mereka dibatasi dengan cara lain.
Elliot Foster
Anda dapat mengonfigurasi Redis untuk menyimpan data secepat yang Anda inginkan (pada jumlah X detik atau pada jumlah perubahan Y).
Buffalo