SQLite “UNIQUE constraint failed” hatası, eklenmek veya güncellenmek istenen değerin tabloda benzersiz olması gereken başka bir değerle çakışmasıyla oluşur. En sık neden, UNIQUE tanımlanmış bir sütuna daha önce kullanılmış bir değerin yeniden yazılmaya çalışılmasıdır.
Çözüm, aynı kaydın tekrar eklenmesinin mi engelleneceğine, mevcut kaydın mı güncelleneceğine ya da işlemin sessizce atlanmasına göre belirlenir. Önce hatanın hangi tablo ve sütundan kaynaklandığını okumak, doğru çözümü seçmenin ilk adımıdır.
UNIQUE kısıtı ve PRIMARY KEY neyi korur?
Kısıt, veritabanında hangi değerlerin kabul edileceğini belirleyen bir kuraldır. UNIQUE kısıtı, bir sütunda veya sütun grubunda aynı değer kombinasyonunun birden fazla satırda bulunmasını engeller. Böylece e-posta gibi belirli bir kayda ait olması gereken bilgiler tekrar etmez.
PRIMARY KEY, yani birincil anahtar, tablodaki her satırı ayırt eden benzersiz kimliktir. uyeler tablosunda id sütunu satırın kimliğini, email sütunu ise aynı e-posta adresiyle birden fazla üyelik açılmasını önlemeyi sağlayabilir.
CREATE TABLE uyeler (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE,
ad TEXT
);
INSERT INTO uyeler (email, ad)
VALUES ('[email protected]', 'Ayşe');
INSERT INTO uyeler (email, ad)
VALUES ('[email protected]', 'Ayşe Yılmaz');
İlk INSERT işlemi e-posta adresini tabloya ekler. İkinci işlemde aynı e-posta tekrar gönderildiği için email sütunundaki UNIQUE kuralı ihlal edilir. Bu duruma çakışma denir: eklenmek istenen değer, mevcut bir benzersizlik kuralıyla karşılaşır.
id ile email aynı amaçla kullanılmaz. id, uygulamanın belirli bir satıra güvenilir biçimde referans vermesine yardımcı olur. email ise iş kuralı gereği benzersiz tutulmak istenen veriyi temsil eder. Bir tabloda birincil anahtar bulunurken birden fazla UNIQUE kısıtı bulunabilir. SQLite belgelerinde bu kısıtların tablo tanımı içindeki kullanım biçimleri açıklanır. SQLite CREATE TABLE belgeleri
Bu hata çoğu durumda veritabanının veri bütünlüğünü koruma tepkisidir. Aynı üyeyi farklı bir satır olarak eklemek kayıtların çoğalmasına yol açabilecekse UNIQUE kısıtını kaldırmak otomatik bir çözüm sayılmaz. Önce uygulamanın aynı e-postayı neden ikinci kez göndermeye çalıştığı incelenmelidir.
Hata mesajındaki tablo ve sütun adı nasıl okunur?

UNIQUE constraint failed: uyeler.email mesajında iki önemli bilgi bulunur. İki nokta üst üste işaretinden sonra gelen uyeler tablo adını, email ise çakışmanın oluştuğu sütunu gösterir.
- Tablo adını oku: İşlemin
uyelertablosunda başarısız olduğunu belirle. - Sütun adını belirle: Tekrar eden değerin
emailsütununda arandığını kontrol et. - Gönderilen değeri incele:
INSERTveyaUPDATEiçinde kullanılan e-posta adresinin daha önce kayıtlı olup olmadığını denetle. - İş akışını kontrol et: Aynı kaydın bir döngü içinde veya aynı isteğin birden fazla çalışmasıyla yeniden gönderilip gönderilmediğine bak.
Hata mesajındaki adlar her zaman email olmayabilir. Örneğin başka bir sütunda UNIQUE kısıtı varsa mesaj o sütunun adını gösterebilir. Birleşik bir kısıtta ise UNIQUE (email, kurum_id) gibi birden fazla sütun birlikte değerlendirilir. Bu nedenle teşhis sırasında yalnızca eklenen değere değil, mesajda yazan tablo ve sütun adlarının tamamına bakmak gerekir.
Eklemeden önce kayıt var mı nasıl kontrol edilir?
Yeni bir üye eklemeden önce SELECT sorgusuyla e-posta adresinin tabloda bulunup bulunmadığını kontrol edebilirsin. Kayıt varsa kullanıcıya mesaj gösterebilir, yoksa INSERT sorgusunu çalıştırabilirsin.
Aşağıdaki örnek, e_posta sütununda UNIQUE kısıtı bulunan bir tablo oluşturur. Bu kısıt, aynı e-posta değerinin birden fazla satırda kullanılmasını engelleyen bir veri tabanı kuralıdır.
import sqlite3
baglanti = sqlite3.connect(":memory:")
baglanti.execute("""
CREATE TABLE uyeler (
id INTEGER PRIMARY KEY,
e_posta TEXT UNIQUE,
ad TEXT
)
""")
baglanti.execute(
"INSERT INTO uyeler (id, e_posta, ad) VALUES (?, ?, ?)",
(1, "[email protected]", "Ada")
)
e_posta = "[email protected]"
kayit = baglanti.execute(
"SELECT id FROM uyeler WHERE e_posta = ?",
(e_posta,)
).fetchone()
if kayit:
print("Bu e-posta adresi zaten kayıtlı.")
else:
baglanti.execute(
"INSERT INTO uyeler (e_posta, ad) VALUES (?, ?)",
(e_posta, "Deniz")
)
baglanti.commit()
print("Üye eklendi.")
baglanti.close()
Örnekte aynı e-posta önceden eklendiği için çıktı Bu e-posta adresi zaten kayıtlı. olur. Sorgulardaki ? işaretleri değerleri SQL metnine eklemek yerine parametre olarak gönderir; bu yaklaşım SQL enjeksiyonu riskini azaltmaya yardımcı olur.
Ön kontrol, kullanıcıya açıklayıcı bir mesaj vermeyi kolaylaştırır. Bununla birlikte kontrol ile ekleme arasında başka bir işlem aynı adresi kaydedebilir; bu yüzden UNIQUE kısıtını korumak ve ekleme sırasında oluşabilecek hatayı da ele almak gerekir.
Hata bir döngü içinde ortaya çıkıyorsa döngünün her turunda hangi e-posta değerinin kullanıldığını kontrol et. Aynı INSERT sorgusu değişmeyen bir değerle tekrar çalışıyorsa kayıtları döngüye almadan önce tekrarlanan değerleri ayıklamayı ve eklemenin doğru yerde yapıldığını inceleyebilirsin.
IGNORE ve REPLACE hangi durumlarda farklı davranır?

INSERT OR IGNORE, e-posta için UNIQUE kısıtıyla çakışan eklemeyi atlar; INSERT OR REPLACE ise çakışmaya neden olan mevcut satırı silip yeni satırı ekler. Çakışma, eklenmek istenen değerin mevcut bir kayıtla kısıtın izin vermediği biçimde eşleşmesidir.
| Yöntem | Çakışmada ne olur? | Ne zaman düşünülebilir? | Temel risk |
|---|---|---|---|
| SELECT ile ön kontrol | Uygulama kaydı bulup eklemeyi atlayabilir. | Kullanıcıya açıklayıcı mesaj göstermek gerektiğinde. | Kontrol ile ekleme arasında veri değişebilir. |
| INSERT OR IGNORE | Çakışan satır eklenmez. | Mevcut kaydı değiştirmeden tekrarları atlamak gerektiğinde. | Eklemenin atlandığı gözden kaçabilir. |
| INSERT OR REPLACE | Çakışan mevcut satır silinir ve yeni satır eklenir. | Eski satırın silinmesi ve yeni satırın eklenmesi gerçekten isteniyorsa. | Eski satırdaki bilgiler veya ilişkili veriler etkilenebilir. |
Önceki örnekteki tabloyu ve mevcut Ada kaydını düşün. Aşağıdaki sorgular aynı e-posta ile sırayla çalıştırıldığında ilk sorgu kaydı korur, ikinci sorgu ise eski satırın yerine yeni satırı ekler:
INSERT OR IGNORE INTO uyeler (id, e_posta, ad)
VALUES (2, '[email protected]', 'Deniz');
INSERT OR REPLACE INTO uyeler (id, e_posta, ad)
VALUES (2, '[email protected]', 'Deniz');
SELECT id, e_posta, ad FROM uyeler;
Son sorgunun çıktısı 2 | [email protected] | Deniz olur. REPLACE sonrasında eski satırın id ve ad değerleri korunmaz; belirtilmeyen başka sütunlar varsa onların eski değerleri de kaybolabilir. Satıra bağlı kayıtlar ve tetikleyiciler de veri tabanının ayarlarına ve tanımlarına göre etkilenebilir.
IGNORE ve REPLACE her tür kısıt hatasını aynı biçimde işlemez; özellikle yabancı anahtar ihlalleri için davranış farklıdır. Tekrarlanan e-postaları kabul etmek amaçlanmıyorsa UNIQUE kısıtını kaldırmak yerine tekrarın kaynağını araştırmak, veri bütünlüğü sorununu görünür tutar.
UPSERT ile mevcut kaydı nasıl güncellersin?
Yeni e-postayı eklemek, aynı e-posta zaten kayıtlıysa da belirli alanları güncellemek istiyorsan UPSERT kullanabilirsin. SQLite’ta bu işlem INSERT ... ON CONFLICT(email) DO UPDATE SET ... biçiminde yazılır ve çakışmayı belirlediğin sütuna göre yönetir.
SQLite UPSERT söz dizimi 3.24.0 sürümüyle tanıtılmıştır. Resmî belgelerde, ON CONFLICT bölümünün çakışmayı hangi UNIQUE veya PRIMARY KEY kuralının tetikleyeceğini belirlediği, excluded ifadesinin ise eklenmek istenen yeni satırdaki değere erişmek için kullanıldığı açıklanır.
INSERT INTO uyeler (email, ad_soyad)
VALUES ('[email protected]', 'Ayşe Yılmaz')
ON CONFLICT(email) DO UPDATE SET
ad_soyad = excluded.ad_soyad;
Tabloda [email protected] adresi zaten varsa yeni satır oluşturulmaz. Bunun yerine o e-posta adresine ait ad_soyad değeri “Ayşe Yılmaz” olarak güncellenir. Başka sütunları değiştirmek istemiyorsan onları DO UPDATE SET bölümüne eklemezsin.
Bu davranış, diğer çakışma seçeneklerinden ayrılır. INSERT OR IGNORE mevcut satırı olduğu gibi bırakır ve yeni veriyi yok sayar. INSERT OR REPLACE çakışan satırı silip yeni satır ekleyebileceği için yeni ekleme içinde bulunmayan alanlar kaybolabilir; satır kimliği ve ilişkili kayıtlar açısından da beklenmeyen sonuçlar doğabilir. UPSERT ise seçtiğin sütunları günceller ve satırın geri kalanını korur.
Python gibi bir uygulamada değerleri SQL metnine birleştirmek yerine ? yer tutucularıyla parametre göndermek gerekir. Böylece kullanıcıdan gelen metin SQL komutunun parçası olarak yorumlanmaz ve SQL enjeksiyonu riskini azaltan doğru sorgulama yöntemi kullanılır.
Python’da IntegrityError nasıl anlaşılır biçimde yakalanır?
Python’un sqlite3 modülünde UNIQUE kuralını ihlal eden bir ekleme, sqlite3.IntegrityError istisnası oluşturur. Bu istisnayı yakalayarak teknik hata yığını göstermek yerine e-posta adresinin zaten kayıtlı olduğunu belirten bir mesaj sunabilirsin.
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("""
CREATE TABLE uyeler (
email TEXT UNIQUE,
ad_soyad TEXT
)
""")
con.execute(
"INSERT INTO uyeler (email, ad_soyad) VALUES (?, ?)",
("[email protected]", "Ayşe Kaya")
)
con.commit()
try:
con.execute(
"INSERT INTO uyeler (email, ad_soyad) VALUES (?, ?)",
("[email protected]", "Ayşe Yılmaz")
)
con.commit()
except sqlite3.IntegrityError:
con.rollback()
print("Bu e-posta adresi zaten kayıtlı.")
Kod çalıştığında beklenen kullanıcı mesajı “Bu e-posta adresi zaten kayıtlı.” olur. rollback(), hata alan işlem sırasında açılmış değişiklikleri geri almak için kullanılır. Hata yakalamak UNIQUE kuralını devre dışı bırakmaz; uygulamanın veri kuralını koruyarak çakışmayı kontrollü biçimde ele almasını sağlar.
- Her e-posta adresinin tekil olması gerekiyorsa UNIQUE kısıtını koru.
- Döngü içinde aynı verinin tekrar tekrar eklenmesine yol açan koşulları kontrol et.
- REPLACE kullanmadan önce satırın silinip yeniden oluşturulmasının sonuçlarını değerlendir.
- Mevcut kayıt güncellenecekse, güncellenecek sütunları açıkça belirleyen UPSERT yaklaşımını tercih et.
Sık Sorulan Sorular
SQLite UNIQUE constraint failed hatası ne anlama gelir?
SQLite UNIQUE constraint failed hatası, UNIQUE veya PRIMARY KEY kuralının izin vermediği bir tekrarın eklenmeye ya da güncellenmeye çalışıldığını gösterir. Örneğin uyeler tablosundaki email sütunu UNIQUE ise aynı e-posta adresiyle ikinci bir kayıt eklenemez.
UNIQUE kısıtını tablodan kaldırmak doğru bir çözüm müdür?
UNIQUE kısıtını kaldırmak, tekil olması gereken verilerin tekrarını mümkün hâle getireceği için genel bir çözüm değildir. Sorun sorgunun döngü içinde tekrarlanmasıysa döngüyü düzeltmek, mevcut kayıt güncellenecekse UPSERT kullanmak daha güvenli yaklaşımlardır.
INSERT OR REPLACE neden veri kaybına yol açabilir?
INSERT OR REPLACE, çakışan satırı güncellemek yerine silebilir ve yeni verilerle başka bir satır oluşturabilir. Yeni ekleme içinde bulunmayan sütun değerleri korunmayabileceği için özellikle ek alanları olan tablolarda veri kaybı riski oluşur.
INSERT OR IGNORE ile UPSERT arasındaki fark nedir?
INSERT OR IGNORE çakışma olduğunda yeni işlemi yok sayar ve mevcut satırı değiştirmez. UPSERT ise çakışan kaydı bulduğunda DO UPDATE SET bölümünde seçtiğin sütunları yeni değerlerle günceller.
UNIQUE hatasını çözerken önce verinin gerçekten tekil olması gerekip gerekmediğini belirle, ardından ekleme, yok sayma veya güncelleme davranışlarından amacına uygun olanı seç.