“IGNORE EKLE” vs “ÇİFT KİLİT ANAHTAR GÜNCELLEMESİNE EKLE…”


833

INSERTBirçok satır içeren bir deyim yürütülürken , başarısızlığa neden olabilecek yinelenen girişleri atlamak istiyorum. Bazı araştırmalardan sonra, seçeneklerim şunlardan birinin kullanımı gibi görünüyor:

  • ON DUPLICATE KEY UPDATE bu da bir maliyetle gereksiz bir güncelleme anlamına gelir veya
  • INSERT IGNORE bu da diğer türden başarısızlıkların habersiz kayma daveti anlamına gelir.

Bu varsayımlarda haklı mıyım? Yinelenenlere neden olabilecek satırları atlamanın ve diğer satırlara devam etmenin en iyi yolu nedir?

Yanıtlar:


990

Kullanmanızı tavsiye ederim INSERT...ON DUPLICATE KEY UPDATE.

Eğer kullanırsanız INSERT IGNOREo yinelenen bir anahtar ile sonuçlanır, sonra satır aslında eklenmez. Ancak ifade bir hata oluşturmaz. Bunun yerine bir uyarı oluşturur. Bu durumlar şunları içerir:

  • PRIMARY KEYVeya UNIQUEkısıtlı sütunlara yinelenen bir anahtar ekleme .
  • NOT NULLKısıtlaması olan bir sütuna NULL ekleme .
  • Bölümlenmiş bir tabloya satır eklemek, ancak eklediğiniz değerler bir bölümle eşleşmiyor.

Eğer kullanırsanız REPLACE, MySQL aslında gelmez DELETEbir takip INSERTiçten, bazı beklenmedik yan etkileri vardır ki:

  • Yeni bir otomatik artış kimliği tahsis edilir.
  • Yabancı anahtarları olan bağımlı satırlar silinebilir (basamaklı yabancı anahtarlar kullanıyorsanız) veya REPLACE .
  • Yanan tetikleyiciler DELETEgereksiz yere yürütülür.
  • Yan etkiler kopyalara da yayılır.

düzeltme: Her iki REPLACEve INSERT...ON DUPLICATE KEY UPDATEstandart dışı, MySQL tescilli buluşlar özgüdür. ANSI SQL 2003 MERGE, aynı ihtiyacı (ve daha fazlasını) çözebilecek bir deyim tanımlar , ancak MySQL MERGEdeyimi desteklemez .


Bir kullanıcı bu yayını düzenlemeye çalıştı (düzenleme denetleyiciler tarafından reddedildi). Düzenleme INSERT...ON DUPLICATE KEY UPDATE, yeni bir otomatik artış kimliğinin tahsis edilmesine neden olan bir hak talebi eklemeye çalıştı . Yeni kimliğin oluşturulduğu doğrudur , ancak değiştirilen satırda kullanılmaz.

Percona Server 5.5.28 ile test edilen aşağıdaki gösteriye bakın. Yapılandırma değişkeni innodb_autoinc_lock_mode=1(varsayılan):

mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   10 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

Yukarıda IODKU deyimi yinelenen algıladığını gösterir ve değerini değiştirmek için güncelleştirmeyi çağırır u. Not AUTO_INCREMENT=3bir kimlik oluşturulduğu gösterir ancak üst üste kullanılmaz.

Oysa REPLACEüreten orijinal satır ve ekler, bir yeni satır silme yapar ve yeni bir otomatik artış kimliği saklama:

mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  3 |   20 |
+----+------+

3
Mysql geliştirme ekibinin ANGE SQL 2003'ten MERGE benimseme niyeti olup olmadığını merak ediyorum.
Lonnie Best

1
@LonnieBest: MERGE'yi uygulamaya yönelik özellik isteği 2005 yılında yapıldı, ancak bildiğim kadarıyla ilerleme veya plan yok. bugs.mysql.com/bug.php?id=9018
Bill

2
Oh, geçersiz tür uyuşmazlığı için uyarılar (hatalar değil) oluşturduğunu ancak yinelenen bileşik birincil anahtar için bir uyarı oluşturduğunu ekleyebilirim.
Fabrício Matté

11
Ben sadece bir sürü INSERT ... ON DUPLICATE KEY UPDATE ...ifade tarafından doldurulmuş bir tabloya bakıyordum . Verilerin çoğu yineleniyor ve AI PK'nin bir örneğinin iki satır arasında 17.029.941'den 46.271.740'a çıkmasıyla sonuçlandı. Her seferinde yeni bir AI nesli, aralığınızın çok hızlı bir şekilde doldurulabileceği ve temizlemeniz gerektiği anlamına gelir. Bu masa sadece iki haftalık!
Mühendis81

4
@AntTheKnee, ahh, Büyük Veri zamanında çalışmanın zorlukları.
Bill Karwin

174

Tüm bunların ne anlama geldiğini görmek istiyorsanız, işte her şeyin bir darbesi:

CREATE TABLE `users_partners` (
  `uid` int(11) NOT NULL DEFAULT '0',
  `pid` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`uid`,`pid`),
  KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Birincil anahtar, bu hızlı başvuru tablosunun her iki sütununu temel alır. Birincil anahtar benzersiz değerler gerektirir.

Hadi başlayalım:

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected

not, yukarıdaki sütun kendini eşit ayarlayarak çok fazla ekstra iş kaydetti, aslında hiçbir güncelleme gerekli

REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected

ve şimdi bazı çok sıralı testler:

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected

konsolda başka ileti oluşturulmadı ve artık tablo verilerinde bu 4 değere sahip. (1,1) hariç her şeyi sildim, böylece aynı oyun alanından test yapabilirim

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected

REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected

İşte burada. Bunların hepsi üretimde olmayan ve neredeyse hiç veri içermeyen yeni bir masa üzerinde gerçekleştirildiğinden, uygulama süreleri mikroskopik ve alakasızdı. Gerçek dünya verilerine sahip olan herkes buna katkıda bulunmaktan memnuniyet duyar.


Ben hem yinelenen anahtar koştu ve yerine. Tablolarım ~ 120K satırları ile sona erdi. Yinelenen anahtar 102 saniyede koştu ve yerine 105 saniyede koştu. Benim durumum için, yinelenen tuşa bağlı kalıyorum.
crunkchitis

1
Yukarıda MariaDB 10 ile test edildi ve çalışırken bir uyarı aldım INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4).
Floris

Tüm bunlar için hangi MySQL sürümünü kullandınız?
Radu Murzea

41

Eklemek için önemli bir şey: INSERT IGNORE kullanırken ve önemli ihlalleriniz varsa, MySQL bir uyarı YAPMAYIN!

Örneğin, biri hatalı biri olmak üzere bir kerede 100 kayıt eklemeyi denerseniz, etkileşimli moda girersiniz:

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

Gördüğünüz gibi: Uyarı Yok! Bu davranış resmi Mysql Belgeleri'nde bile yanlış tanımlanmıştır.

Betiğinizin bilgilendirilmesi gerekiyorsa, bazı kayıtlar eklenmediyse (anahtar ihlalleri nedeniyle) mysql_info () öğesini çağırmalı ve "Yinelenen" değer için ayrıştırmalısınız.


6
PHP kullanıyorsanız mysqli_affected_rows(), INSERTgerçekten olup olmadığını bilmek için kullanmanız gerekir .
Amal Murali

MySQL 5.5 ve mariadb 10 hem Tam ben yapmak bir hata alıyorum Cannot add or update a child row: a foreign key constraint fails ve hiçbir satır (hatta geçerli olanları) eklenir.
Floris

2
@Floris Bu hata, yabancı bir anahtar kısıtlamasından kaynaklanır ve yinelenen bir anahtardan kaynaklanmaz . MySQL 5.5.28 kullanıyorum. Kullanırken INSERT IGNORE, yinelenen anahtarlar hata veya uyarı olmadan yok sayılır.
toxalot

20

Ben rutin olarak kullanıyorum INSERT IGNOREve tam da aradığınız davranış gibi geliyor. Dizin çakışmalarına neden olacak satırların eklenmeyeceğini ve programınızı buna göre planladığınızı bildiğiniz sürece, herhangi bir soruna neden olmamalıdır.


4
Çoğaltma dışındaki hataları yok sayacağımdan endişeliyim. Bu doğru mu veya INSERT IGNORE yoksayma yalnızca çoğaltma hatasını yoksayar mı? Teşekkürler!
Thomas G Henry

2
Herhangi bir hatayı bir uyarıya dönüştürür. Cevabımda bu tür vakaların bir listesine bakın.
Bill Karwin

Bu utanç verici; Keşke sadece yinelenen hataları görmezden gelsin.
Lonnie Best

Anahtar ihlalleri hatalara neden olur ! @Jens'in cevabındaki yorumuma bakın.
Floris

1
@Pacerier, uygulamanızın uyarıları kontrol edip etmediğine bağlıdır. Veya uyarıları kontrol edip edemediği . Örneğin, çoğu ORM paketi size fırsat tanımaz. Bazı konektörler (örneğin JDBC) sizi MySQL API'sinden ayırır, böylece uyarıları kontrol etme şansınız olmaz.
Bill Karwin

18

Bunun eski olduğunu biliyorum, ancak INSERT..IGNORE hakkında bilgi bulmaya çalışırken başkalarının (benim gibi) bu sayfaya gelmesi durumunda bu notu ekleyeceğim.

Yukarıda belirtildiği gibi, INSERT..IGNORE kullanırsanız, INSERT deyimi yürütülürken oluşan hatalar uyarı olarak kabul edilir.

Açıkça belirtilmeyen bir şey, INSERT..IGNORE öğesinin geçersiz değerlerin eklendiğinde en yakın değerlere ayarlanmasına neden olacağıdır (geçersiz değerler IGNORE anahtar sözcüğü kullanılmazsa sorgunun iptal edilmesine neden olur).


6
"Geçersiz değerler" ile ne kastettiğinizden ve neyle düzeltildiğinizden gerçekten emin değilim? Bir örnek veya daha fazla açıklama verebilir misiniz?
Marenz

4
Bu, "IGNORE INSERT" kullanırken bir alana yanlış veri türü eklerseniz, verilerin alanın veri türüyle eşleşecek şekilde değiştirileceği ve potansiyel olarak geçersiz bir değerin ekleneceği, ardından sorgunun çalışmaya devam edeceği anlamına gelir. Yalnızca "INSERT" ile yanlış veri türü hakkında bir hata ortaya çıkar ve sorgu iptal edilir. Bir sayı varchar veya metin alanına eklenirken sorun olmayabilir, ancak sayısal veri türüne sahip bir alana metin dizesi eklemek hatalı verilere neden olur.
codewaggle

2
@Marenz başka bir örnek: tablonuzda boş olmayan bir sütun varsa ve "INSERT IGNORE" sorgunuz bu sütun için bir değer belirtmezse, katı sql_mode'un etkin olup olmadığına bakılmaksızın, satır bu sütuna sıfır değeriyle eklenir .
Shannon

Geçersiz değerler hakkında iyi bir nokta! Bu konu "IGNORE INSERT" hakkında bilgi edinmek için harikadır, 5 sentimi de bırakacağım: " IGERORE INSERT" kullanırken ne kadar dikkatli olmanız gerektiğine dair örnekler içeren medium.com/legacy-systems-diary/… güzel makale Beyan.
0x49D1

8

DUPLICATE ÜZERİNDE ANAHTAR GÜNCELLEME gerçekten standart değildir. REPLACE kadar standarttır. Bkz. SQL MERGE .

Esasen her iki komut da standart komutların alternatif sözdizimi versiyonlarıdır.


1
replace bir silme ve ekleme işlemi yaparken, yinelenen anahtar güncellemesi varolan satırı günceller. bazı farklılıklar şunlardır: otomatik artan kimlik, satır konumu, bir sürü tetikleyici
ahnbizcad

8

ReplaceInto bir seçenek gibi görünüyor. Veya kontrol edebilirsiniz

IF NOT EXISTS(QUERY) Then INSERT

Bu eklenir veya silinir ve eklenir. IF NOT EXISTSÖnce bir çeke gitme eğilimindeyim .


Hızlı cevap için teşekkürler. Ben her yerde varsayıyorum, ama bu gereksiz güncelleme gerçekleştirecek şekilde ON DUPLICATE KEY UPDATE benzer olacağını varsayalım. Bu savurgan görünüyor, ama emin değilim. Bunlardan herhangi birinin çalışması gerekir. Hangisinin en iyi olduğunu bilen biri olup olmadığını merak ediyorum.
Thomas G Henry

6
NTuplip - bu çözüm, eşzamanlı işlemlerle uçlardan gelen yarış koşullarına hala açıktır.
Chris KL

REPLACEeşleşen tablodaki tüm satırları siler herhangi PRIMARY veya UNIQUEanahtar sonra INSERTs . Bu potansiyel olarak IODKU'dan çok daha fazla iş.
Rick James

4

INSERT IGNORE'un potansiyel tehlikesi. VARCHAR değerini daha uzun eklemeye çalışıyorsanız, sütun ile tanımlandı - değer kısaltılacak ve katı mod etkinleştirilirse EVEN eklenecektir.


3

Kullanılıyorsa insert ignorebir sahip SHOW WARNINGS;kimlikleri tekrarı olan hangi dahil tüm uyarılara, bir tablo gösterecektir sorgu set sonunda açıklama.


SHOW WARNINGS;yalnızca en son sorguyu etkiler. Tek bir ifadeden daha fazlasına sahipseniz, önceki ifadeler birikmez.
Kawu

2

Tabloya ve birincil anahtarın veya benzersiz dizinin çakışmasına eklemek istiyorsanız, o satırı eklemek yerine çakışan satırı güncelleyecektir.

Sözdizimi:

insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;

Şimdi, bu insert ifadesi daha önce gördüklerinizden farklı görünebilir. Bu insert deyimi, sütun1 ve sütun2'ye sırasıyla a ve b değeriyle bir satır eklemeye çalışmaktadır.

Bu ifadeyi derinlemesine anlayalım:

Örneğin: burada sütun1, tablo1'deki birincil anahtar olarak tanımlanır.

Şimdi table1'de sütun1'de "a" değerine sahip bir satır yoksa. Yani bu ifade table1'e bir satır ekleyecektir.

Tablo1'de, sütun2'de "a" değerine sahip bir satır varsa. Bu ifade, satırın sütun2 değerini sütun1 değerinin "a" olduğu "c" ile güncelleyecektir.

Bu nedenle, yeni bir satır eklemek istiyorsanız, aksi takdirde birincil anahtarın veya benzersiz dizinin çakışmasıyla ilgili satırı güncelleyin.
Bu bağlantı hakkında daha fazla bilgi edinin


0

INSERT...ON DUPLICATE KEY UPDATE beklenmedik İstisnalar yönetimini önlemek için tercih edilir.

Bu çözüm yalnızca ** 1 benzersiz kısıtlamanız ** olduğunda çalışır

Benim durumumda bunu biliyorum col1vecol2 benzersiz bir bileşik endeks oluşturuyorum.

Hatayı izler, ancak yinelenen bir istisna atmaz. Performansla ilgili olarak, MySQL ile aynı değerde güncelleme yapmak etkilidir ve bunu güncellemez

INSERT INTO table
  (col1, col2, col3, col4)
VALUES
  (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
    col1 = VALUES(col1),
    col2 = VALUES(col2)

Bu yaklaşımı kullanma fikri phpdelusions.net/pdo adresindeki yorumlardan geldi .

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.