SQLite INSERT - ÇİFT ANAHTAR GÜNCELLEMESİNDE (UPSERT)


98

MySQL'de şunun gibi bir şey var:

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

Bu özelliğin SQLite'de olmadığını bildiğim kadarıyla, bilmek istediğim şey, iki sorgu yürütmek zorunda kalmadan aynı etkiyi elde etmenin bir yolu olup olmadığıdır. Ayrıca, bu mümkün değilse, neyi tercih edersiniz:

  1. SELECT + (INSERT veya UPDATE) veya
  2. GÜNCELLEME ( UPDATE başarısız olursa + INSERT )

Yanıtlar:



118
INSERT OR IGNORE INTO visits VALUES ($ip, 0);
UPDATE visits SET hits = hits + 1 WHERE ip LIKE $ip;

Bu, "ip" sütununun UNIQUE (veya PRIMARY KEY) kısıtlamasına sahip olmasını gerektirir.


DÜZENLEME: Başka bir harika çözüm: https://stackoverflow.com/a/4330694/89771 .


2
Sadece kayıt için, REPLACEbir seçenek değil.
Alix Axel

1
"Başka bir harika çözüm" bağlantısıyla ilgili olarak, aynı soruya farklı bir yanıt vermeyi de
düşünürdüm

19

Tercih ederim UPDATE (+ INSERT if UPDATE fails). Daha az kod = daha az hata.


1
Teşekkürler! @Sam ( stackoverflow.com/questions/418898/… ) sizinle aynı fikirde görünüyor. Ben de bu yaklaşımı tercih ediyorum.
Alix Axel

@Smith Düzgün UPDATE ve INSERT ifadeleri kullanmayı ve dönüş değerini kontrol etmeyi kastettim.
codeholic

Bunun atomikliği yoktur, eğer arasına başka bir işlem eklenirse INSERT'in başarısız olması mümkündür.
Robin Lavallée

7

Mevcut cevap sadece sqlite OR mysql'de çalışacaktır (OR kullanıp kullanmamanıza bağlı olarak). Dolayısıyla, çapraz dbms uyumluluğu istiyorsanız, aşağıdakiler yapacaktır ...

REPLACE INTO `visits` (ip, value) VALUES ($ip, 0);

3
Kabul edilen cevap SQLite üzerinde çalışıyor (amacım buydu). REPLACESQLite üzerinde de çalışacaktır, ancak MySQL'de sayacı her zaman 0'a sıfırlayacaktır - sorgu taşınabilirken, sonuç çok farklı olacaktır.
Alix Axel

Haklısın, OP'nin taşınabilir bir şey aradığını sanıyordum. REPLACE INTO'nun, özellikle PK korumasının gerekli olduğu tüm durumlarda çalışmayacağını, ancak çoğu durumda işe yarayacağını fark ettim.
Jacob Thomason

Verileri atmak yerine temiz bir şekilde başarısız olmak bir özelliktir, bir hata değil.
Tobu

-4

Bunun için memcached'i kullanmalısınız çünkü bu tek bir değeri (ziyaret sayısı) depolayan tek bir anahtar (IP adresi). "Yarış" koşullarının olmadığından emin olmak için atomik artış işlevini kullanabilirsiniz.

MySQL'den daha hızlıdır ve yükü azaltır, böylece MySQL başka şeylere odaklanabilir.


Veriler o kadar önemli değilse, evet. Ancak, bu, birçok IP'nin hizmete çarptığı yoğun bir sitede kullanılıyorsa, memcached örnekleri dolabilir ve bazı içeriklerin atılmasına neden olabilir. Memcached içeriğini yedeklemek de ilginç olabilir (gerekirse).
Elliot Foster

@ElliotFoster Memcached, ona attığınız RAM kadar çok veriyi işleyebilir (sebat etmek istiyorsanız ayrıca redis veya membase kullanın). Günde 1 milyondan fazla ziyaretçi alıyorsanız, muhtemelen memcache örneğinize 30MB'den fazla ram vermeyi göze alabilirsiniz (ki sanırım varsayılan budur). Ancak, verdiğiniz bellek miktarı açısından SQLite ve MySQL'den çok daha yüksek bir yükü kesinlikle kaldırabilir - hiçbir karşılaştırma yoktur.
Xeoncross

Harika bir araç olduğunu düşündüğüm için lütfen yorumumu memcache aleyhine bir oy olarak yanıltmayın. Redis de olduğu gibi (Kullanmadığım için membase adına konuşamam.) Ancak memcache / redis en güvenilir mağazalar değil. Evet, redis'in kalıcılığı var, ancak veriler belirli bir aralıkta (son baktığımda) ve memcache'de diskte saklanıyor. Dediğim gibi, veriler önemli değilse (veya kolayca yeniden üretilebiliyorsa) memcache ve şirket harikadır. Orijinal gönderi ayrıca MySQL'den çok farklı olan ve muhtemelen başka şekillerde sınırlı oldukları anlamına gelen sqlite hakkında da soruyordu.
Elliot Foster

Redis'i verileri istediğiniz kadar hızlı saklaması için yapılandırabilirsiniz (X saniyede veya Y sayıda değişiklik olduğunda).
Buffalo
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.