BAŞKA GÜNCELLEME VARSA EKLE?


275

Klasik "birkaç" olurdu "çözümler buldum Nasıl yeni bir kayıt eklemek veya zaten varsa bir güncelleme" ama SQLite çalışmak için bunların hiçbirini alamıyorum.

Aşağıdaki gibi tanımlanmış bir tablo var:

CREATE TABLE Book 
ID     INTEGER PRIMARY KEY AUTOINCREMENT,
Name   VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level  INTEGER,
Seen   INTEGER

Ne yapmak istiyorum, benzersiz bir isim ile bir kayıt eklemek. Ad zaten varsa, alanları değiştirmek istiyorum.

Birisi bana bunu nasıl yapacağımı söyleyebilir mi lütfen?


3
"ekle veya değiştir", "ekle veya güncelle" den tamamen farklıdır
Fattie

Yanıtlar:


320

Http://sqlite.org/lang_conflict.html adresine bir göz atın .

Gibi bir şey istiyorsun:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);

Satır tabloda zaten varsa, ekleme listesinde olmayan herhangi bir alanın NULL olarak ayarlanacağını unutmayın. Bu nedenle IDsütun için bir alt seçim vardır : Yeni durumda ifade, bunu NULL olarak ayarlar ve ardından yeni bir kimlik atanır.

Bu yaklaşım, değiştirme durumundaki satır ancak alan ekleme durumunda alanı NULL olarak ayarladıysa belirli alan değerlerini yalnız bırakmak istiyorsanız da kullanılabilir.

Örneğin, Seenyalnız bırakmak istediğinizi varsayarsak :

insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
   (select ID from Book where Name = "SearchName"),
   "SearchName",
    5,
    6,
    (select Seen from Book where Name = "SearchName"));

109
Yanlış "ekle veya değiştir", "ekle veya güncelle" den farklı. Geçerli bir yanıt için bkz. Stackoverflow.com/questions/418898/…
rds

12
@rds Hayır, yanlış değil çünkü bu soru "alanları değiştir" diyor ve birincil anahtar sütun listesinin bir parçası değil, ancak diğer tüm alanlar. Tüm alan değerlerini değiştirmediğiniz köşe vakalarınız olacaksa veya birincil anahtarla uğraşıyorsanız farklı bir şey yapmalısınız. Eksiksiz yeni alanlar kümeniz varsa bu yaklaşım gayet iyi. Göremediğim belirli bir sorunun mu var?
janm

9
Tüm alanlar için tüm yeni değerleri biliyorsanız geçerlidir. Yalnızca kullanıcı güncellemesi, diyelim ki Level, bu yaklaşım izlenemez.
rds

4
Doğru . Diğer cevap önemlidir, ancak bu yaklaşım tüm alanları (anahtar hariç) güncellemek için geçerlidir.
Ярослав Рахматуллин

2
Evet Bu tamamen yanlış. Sadece yeni bir Çatışma tek sütun değerini güncellemek istiyorum ne olacak. Yukarıdaki durumda, diğer tüm veriler doğru olmayan yeni verilerle değiştirilecektir.
Mrug

85

Sen kullanmalıdır INSERT OR IGNOREbir takip komutu UPDATEaşağıdaki örnekte: komuta namebirincil anahtardır:

INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'

İlk komut kaydı ekler. Kayıt varsa, varolan bir birincil anahtarla çakışmanın neden olduğu hatayı yok sayar.

İkinci komut kaydı güncelleyecektir (şimdi kesinlikle var)


6
hangi anda görmezden gelecek? isim ve yaş aynı olduğunda?
mou

Bu çözüm olmalı ... kesici uçta herhangi bir tetikleyici kullanıyorsanız, kabul edilen cevap her seferinde tetiklenir. Bu sadece bir güncelleme yapmaz ve gerçekleştirir
PodTech.io

1
Yalnızca isme göre yok sayar. Yalnızca "ad" sütununun birincil anahtar olduğunu unutmayın.
Gabriel Ferrer

3
Kayıt yeni olduğunda, güncelleme gerekli değildir, ancak yine de yürütülecek ve bu da kötü performansa neden olacak mı?
MarcG

Bunun için hazırlanmış bir ifade nasıl yürütülür?
prashant

65

Bir değiştirme yaparak daha sonra çözdüğünüz bir " çakışmayı " tetiklemek için tabloda bir kısıtlama ayarlamanız gerekir :

CREATE TABLE data   (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);

Ardından aşağıdakileri yapabilirsiniz:

INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);

"SELECT * FROM data" size şunları verecektir:

2|2|2|3.0
3|1|2|5.0

REPLACE, UPDATE değil DELETE ve INSERT yaptığı için data.id öğesinin "3" değil "1" olduğunu unutmayın. Bu ayrıca gerekli tüm sütunları tanımladığınızdan emin olmanız gerektiği anlamına gelir; aksi takdirde beklenmeyen NULL değerleri alırsınız.


33

Öncelikle güncelleyin. Eğer etkilenen satır sayısı = 0, sonra takın. Tüm RDBMS için en kolay ve uygundur .


11
Veritabanından bağımsız olarak, doğru işlem düzeyindeki bir işlemle iki işlem sorun olmamalıdır.
Janm

5
Insert or Replacegerçekten daha çok tercih edilir.
MPelletier

1
BT belgelerinin daha fazla örnek içermesini diliyorum. Aşağıdaki denedim ve işe yaramaz (benim sözdizimi açıkça yanlış). Ne olması gerektiği hakkında bir fikrin var mı? ÇATIŞMA DEĞİŞTİRME ÜZERİNE (İsim, TypeID, Seviye, Görülen) DEĞERLER ('Süpermen', '2', '14', '0') 2 ',' 14 ',' 0 ')
SparkyNZ

6
Ayrıca, satır değerleri tamamen aynı ise, etkilenen satır sayısı sıfır olur ve yeni bir yinelenen satır oluşturulur.
barkside

2
+1, çünkü INSERT OR REPLACE çakışmadaki orijinal satırı sileceğinden ve tüm sütunları ayarlamazsanız orijinal değerleri kaybedersiniz
Pavel Machyniak

20

INSERT OR REPLACE diğer alanları varsayılan değere değiştirecektir.

sqlite> CREATE TABLE Book (
  ID     INTEGER PRIMARY KEY AUTOINCREMENT,
  Name   TEXT,
  TypeID INTEGER,
  Level  INTEGER,
  Seen   INTEGER
);

sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);
sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');

sqlite> SELECT * FROM Book;
1001|SQLite|||

Diğer alanı korumak istiyorsanız

sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001);
sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;

sqlite> SELECT * FROM Book;
1001|SQLite|10|10|0

veya UPSERT kullanılarak (sözdizimi 3.24.0 (2018-06-04) sürümüyle SQLite'a eklendi)

INSERT INTO Book (ID, Name)
  VALUES (1001, 'SQLite')
  ON CONFLICT (ID) DO
  UPDATE SET Name=excluded.Name;

excluded.Öneki değere eşit VALUES.


16

Upsert istediğiniz şeydir. UPSERTsözdizimi SQLite'a 3.24.0 (2018-06-04) sürümü ile eklendi.

CREATE TABLE phonebook2(
  name TEXT PRIMARY KEY,
  phonenumber TEXT,
  validDate DATE
);

INSERT INTO phonebook2(name,phonenumber,validDate)
  VALUES('Alice','704-555-1212','2018-05-08')
  ON CONFLICT(name) DO UPDATE SET
    phonenumber=excluded.phonenumber,
    validDate=excluded.validDate
  WHERE excluded.validDate>phonebook2.validDate;

Bu noktada gerçek "UPSERT" kelimesinin upert sözdiziminin bir parçası olmadığı konusunda uyarılmalıdır.

Doğru sözdizimi

INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...

ve INSERT INTO SELECT ...en azından WHERE truebelirleme ONsözdizimi ile belirteç hakkında ayrıştırıcı belirsizliğini çözmek için seçim ihtiyaçlarınızı yapıyorsanız .

Uyardı INSERT OR REPLACE...Eğer yabancı anahtar kaskadlar veya diğer silme tetikleyicileri varsa kötü olabilir ki, yerine varsa yenisini takmadan önce kayıt silecektir.


Belgelerde var ve 3.25.1 olan en son sqlite sürümüne sahibim ama benim için çalışmıyor
Bentaiba Miled Basma

yukarıdaki iki sorguyu kelimesi kelimesine denedin mi?
ComradeJoecool

Ubuntu 18.04'ün 3.25 ile değil SQLite3 v3.22 ile geldiğini ve bu nedenle UPSERTsözdizimini desteklemediğini unutmayın .
starbeamrainbowlabs

Özellikteki referans sürümü için teşekkürler!
Matteo

6

Birincil anahtarınız yoksa, yoksa ekleyebilirsiniz, ardından bir güncelleme yapabilirsiniz. Bu tabloyu kullanmadan önce en az bir giriş içermelidir.

INSERT INTO Test 
   (id, name)
   SELECT 
      101 as id, 
      'Bob' as name
   FROM Test
       WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;

Update Test SET id='101' WHERE name='Bob';

Yinelenen girişler oluşturmadan benim için çalışan tek çözüm budur. Muhtemelen kullandığım tablonun birincil anahtarları yoktur ve varsayılan değerleri olmayan 2 sütun vardır. Biraz uzun bir çözüm olmasına rağmen, işi düzgün bir şekilde yapar ve beklendiği gibi çalışır.
Inbar Rose

4

UPSERT'yi istediğine inanıyorum .

Bu yanıtta ek bir hile olmadan "INSERT OR REPLACE", belirtmediğiniz alanları NULL veya başka bir varsayılan değere sıfırlar. (INSERT VEYA DEĞİŞTİRME'nin bu davranışı GÜNCELLEME gibi değildir; tıpkı INSERT gibidir, çünkü aslında INSERT'dir; ancak, eğer UPDATE (eğer GÜNCELLEME) varsa, muhtemelen UPDATE semantiğini istersiniz ve gerçek sonuçtan hoş olmayan bir şekilde şaşıracaksınız.)

Önerilen UPSERT uygulamasındaki hile temelde INSERT OR REPLACE kullanmaktır, ancak değiştirmek istemediğiniz alanlar için geçerli değeri almak üzere katıştırılmış SELECT yan tümcelerini kullanarak tüm alanları belirtir.


1

PRIMARY KEY ve UNIQUE'un nasıl etkileşime girdiğini tam olarak anlamadıysanız, burada beklenmedik bir davranış olabileceğini belirtmek gerekir .

Örnek olarak, yalnızca NAME alanı henüz alınmadıysa bir kayıt eklemek istiyorsanız ve varsa, size bildirmek için bir kısıtlama istisnasının tetiklenmesini istiyorsanız, INSERT VEYA DEĞİŞTİRME atmayacak ve istisna yapmayacak ve bunun yerine çakışan kaydı (varolan kayıt aynı NAME ile ) değiştirerek UNIQUE kısıtlamasını kendiliğinden çözer . Gaspard's bunu yukarıdaki cevabında gerçekten iyi gösteriyor .

Bir kısıtlama istisnasının tetiklenmesini istiyorsanız, bir INSERT deyimi kullanmanız ve adın alınmadığını bildikten sonra kaydı güncellemek için ayrı bir UPDATE komutuna güvenmeniz gerekir .

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.