Ders 04 / 14
Bütünlük Kısıtları
Alan, varlık ve başvuru bütünlüğünün tanımı; yabancı anahtarın silme ve güncelleme eylemleri; kısıt denetiminin zamanlaması ve kısıtın şemaya yazılmasının gerekçesi.
İçindekiler
Önceki ders bir yabancı anahtar ihlalini reddetti ama arkasındaki kuralı adlandırmadı. Bu dersin sorusu şudur: veritabanının her an sağlaması gereken kurallar nelerdir, bu kurallar şemada nasıl yazılır, ve kuralı bozacak bir silme geldiğinde sistemin seçebileceği davranışlar hangileridir?
Kısıtlar üç katmanda toplanır. Alan bütünlüğü (domain integrity) bir sütuna hangi
değerlerin yazılabileceğini söyler — tip, NOT NULL ve CHECK bunun araçlarıdır ve
önceki derste görüldü. Varlık bütünlüğü (entity integrity) her satırın kimliğinin
belirli olmasını ister. Başvuru bütünlüğü (referential integrity) bir bağıntıdan
diğerine yapılan başvurunun boşa düşmemesini ister. Dördüncü bir katman, alana özgü iş
kurallarıdır; bunlar da mümkün olduğunca şemaya yazılır.
Varlık Bütünlüğü
Kural tek cümledir: birincil anahtarı oluşturan hiçbir sütun boş değer alamaz. Gerekçesi doğrudan anahtarın tanımından gelir. Anahtarın işi satırı ayırt etmektir; boş değer ise “bilinmiyor” demektir. Kimliği bilinmeyen bir satır, kendisine başvurulamayan bir satırdır.
Bu kuralın standartta yazılı olması, her motorun onu aynı sıkılıkta uyguladığı anlamına gelmez:
sqlite3 :memory: <<'SQL' .headers on .mode box CREATE TABLE sube (sube_kodu TEXT PRIMARY KEY, ad TEXT NOT NULL); INSERT INTO sube VALUES (NULL, 'Adsız Şube'); SELECT COUNT(*) AS satir FROM sube; SQL
┌───────┐ │ satir │ ├───────┤ │ 1 │ └───────┘
Birincil anahtarı boş olan bir satır tabloya girdi. Bu davranış motora göre değişir; kimi motor aynı deyimi reddeder. Taşınabilir ve niyeti açık şema, kuralı kendi yazar:
sqlite3 :memory: <<'SQL' CREATE TABLE sube (sube_kodu TEXT PRIMARY KEY NOT NULL, ad TEXT NOT NULL); INSERT INTO sube VALUES (NULL, 'Adsız Şube'); SQL
Runtime error near line 2: NOT NULL constraint failed: sube.sube_kodu (19)
Buradan çıkan genel ilke, kursun tekrar eden temalarından biridir: bir kuralın motorda zaten var olduğunu varsaymak yerine şemada açıkça yazmak, hem taşınabilirliği hem okunabilirliği artırır.
Başvuru Bütünlüğü
Kural şudur: bir yabancı anahtarın değeri ya boştur, ya da başvurduğu bağıntıda o değere sahip bir satır vardır. Üçüncü bir olasılık yoktur; hedefi bulunmayan satıra yetim kayıt (orphan record) denir.
Bu kuralın uygulanıp uygulanmadığı da motora göre değişir. Burada kullanılan komut satırı aracında denetim varsayılan olarak kapalıdır ve oturum başına açılması gerekir:
sqlite3 :memory: <<'SQL' .headers on .mode box PRAGMA foreign_keys; CREATE TABLE uye (uye_no INTEGER PRIMARY KEY, ad TEXT NOT NULL); CREATE TABLE odunc ( odunc_no INTEGER PRIMARY KEY, uye_no INTEGER NOT NULL REFERENCES uye (uye_no) ); INSERT INTO odunc VALUES (1001, 77); SELECT COUNT(*) AS yetim_kayit FROM odunc; SQL
┌──────────────┐ │ foreign_keys │ ├──────────────┤ │ 0 │ └──────────────┘ ┌─────────────┐ │ yetim_kayit │ ├─────────────┤ │ 1 │ └─────────────┘
Şemada REFERENCES yazılıdır, üye tablosu boştur, buna rağmen ödünç kaydı girmiştir.
Denetim kapalı olduğu için bildirim yalnızca belgeleme değeri taşımıştır. Bu dersin geri
kalanındaki bloklar denetimi açan satırla başlar.
Silme ve Güncelleme Eylemleri
Başvuru bütünlüğü bozulabilecek iki an vardır: başvurulan satır silindiğinde ve başvurulan anahtar değeri güncellendiğinde. Şema, bu iki an için bir davranış seçer.
NO ACTION/RESTRICT— işlem reddedilir. Standarttaki varsayılan davranıştır; ikisi arasındaki fark denetimin zamanlamasındadır.CASCADE— silme ya da güncelleme, başvuran satırlara yayılır.SET NULL— başvuran satırın yabancı anahtarı boşaltılır; sütunun boş değer kabul etmesi gerekir.SET DEFAULT— başvuran satırın yabancı anahtarı sütunun öntanımlı değerine çekilir; o değerin hedefte bulunması gerekir.
Seçim keyfî değildir, alanın anlamını izler. Üye silindiğinde onun ceza kayıtlarının
kalması anlamsızdır — bunlar üyeye bağımlı, kendi başına var olamayan kayıtlardır, yani
CASCADE. Şube kapandığında üyenin kaydı silinmemelidir; üye şubesiz kalır, yani
SET NULL. Üyeye ait ödünç geçmişi ise silinmemelidir — geçmiş kayıt bir olgudur — bu
yüzden silme reddedilir.
sqlite3 :memory: <<'SQL' .headers on .mode box .nullvalue (bos) PRAGMA foreign_keys = ON; CREATE TABLE sube (sube_kodu TEXT PRIMARY KEY NOT NULL, ad TEXT NOT NULL); CREATE TABLE uye ( uye_no INTEGER PRIMARY KEY, ad TEXT NOT NULL, sube_kodu TEXT REFERENCES sube (sube_kodu) ON DELETE SET NULL ); CREATE TABLE ceza ( ceza_no INTEGER PRIMARY KEY, uye_no INTEGER NOT NULL REFERENCES uye (uye_no) ON DELETE CASCADE, tutar INTEGER NOT NULL ); INSERT INTO sube VALUES ('MRK', 'Merkez'), ('BHC', 'Bahçelievler'); INSERT INTO uye VALUES (41, 'Ayşe Demir', 'MRK'), (52, 'Mert Kaya', 'BHC'); INSERT INTO ceza VALUES (9001, 52, 30), (9002, 52, 15); DELETE FROM uye WHERE uye_no = 52; DELETE FROM sube WHERE sube_kodu = 'MRK'; SELECT (SELECT COUNT(*) FROM uye) AS uye_satiri, (SELECT COUNT(*) FROM ceza) AS ceza_satiri, (SELECT sube_kodu FROM uye WHERE uye_no = 41) AS uye41_sube; SQL
┌────────────┬─────────────┬────────────┐ │ uye_satiri │ ceza_satiri │ uye41_sube │ ├────────────┼─────────────┼────────────┤ │ 1 │ 0 │ (bos) │ └────────────┴─────────────┴────────────┘
Tek bir üye silindi ve ona bağlı iki ceza kaydı da gitti; şube silindi ve ona bağlı üye
şubesiz kaldı. .nullvalue ayarı, boş değeri çıktıda görünür kılmak içindir — ayar
verilmediğinde boş değer ile boş dizgi ekranda ayırt edilemez.
Öntanımlı davranış ise reddetmektir:
sqlite3 :memory: <<'SQL' PRAGMA foreign_keys = ON; CREATE TABLE uye (uye_no INTEGER PRIMARY KEY, ad TEXT NOT NULL); CREATE TABLE odunc ( odunc_no INTEGER PRIMARY KEY, uye_no INTEGER NOT NULL REFERENCES uye (uye_no) ); INSERT INTO uye VALUES (41, 'Ayşe Demir'); INSERT INTO odunc VALUES (1001, 41); DELETE FROM uye WHERE uye_no = 41; SQL
Runtime error near line 9: FOREIGN KEY constraint failed (19)
CASCADE seçiminde dikkatli olmak gerekir: yayılma zincirlenir. Üyeyi silen bir deyim,
ona bağlı ceza kayıtlarını, onlara bağlı ödeme kayıtlarını ve bu zincirin devamını da
siler. Silinen satır sayısı, deyimi yazanın gördüğünden çok daha büyük olabilir.
Kısıt Ne Zaman Denetlenir
Kısıtlar öntanımlı olarak her deyimin sonunda denetlenir. Bu, bazı doğru durumların geçici olarak kurulamamasına yol açar. İki bağıntı birbirine karşılıklı başvuruyorsa — her şubenin bir yöneticisi vardır ve her yönetici bir şubede çalışır — hangisi önce eklenirse eklensin, ekleme anında karşı taraf henüz yoktur.
Çözüm, denetimi işlemin sonuna ertelemektir; buna ertelenmiş kısıt (deferred constraint) denir. Ertelenmiş denetimde ara durumlar kuralı bozabilir, yeter ki işlem tamamlandığında kural sağlansın. Bu yeteneğin bulunup bulunmadığı ve nasıl bildirildiği motora göre değişir; bulunmayan motorlarda karşılıklı başvurunun bir ucunun boş değer kabul etmesi ve iki adımda doldurulması gerekir.
Kısıtı Nereye Yazmalı
Her kural şemaya sığmaz. “Bir üye aynı anda en çok beş nüsha ödünç alabilir” kuralı, tek satıra bakarak denetlenemez; sayım gerektirir. Böyle kurallar ya bir tetikleyiciyle ya da uygulama katmanında yazılır.
Ölçüt şudur: kural veriye erişen bütün yollar için geçerliyse ve tek bir satır ya da satır kümesi üzerinden ifade edilebiliyorsa şemaya yazılır. Şemadaki kısıt, hangi programın yazdığından, hangi dilde yazıldığından ve veriye elle mi yoksa toplu göçle mi dokunulduğundan bağımsız olarak uygulanır. Uygulama katmanındaki denetim yalnızca o uygulamadan geçen yazmaları kapsar; ikinci bir servis, bir bakım betiği ya da elle çalıştırılan bir deyim onu atlar.
Kısıtın ikinci işlevi belgelemedir. Şemayı okuyan biri, ON DELETE CASCADE yazan bir
satırdan ceza kaydının üyeye bağımlı olduğunu okur. Aynı bilgi uygulama kodunda dağınık
biçimde durduğunda okunmaz.
Özet
- Varlık bütünlüğü birincil anahtarın boş değer alamamasıdır; kimliği belirsiz satıra başvurulamaz.
- Başvuru bütünlüğü, yabancı anahtarın ya boş olmasını ya da hedefte karşılığı bulunmasını ister; ihlali yetim kayıt üretir.
- Motorun bir kuralı öntanımlı olarak uyguladığını varsaymak yerine kısıtı şemada açıkça yazmak taşınabilirliği ve okunabilirliği artırır.
- Silme ve güncelleme eylemleri — reddetme, yayma, boşaltma, öntanımlıya çekme — alanın anlamına göre seçilir; yayma zincirlenerek beklenenden çok satır siler.
- Bütün yazma yollarını bağlayan ve satır düzeyinde ifade edilebilen kurallar şemaya, sayım ya da geçmiş gerektirenler uygulama katmanına yazılır.
Sonraki Adım
Bu derste iki kez “ya boştur” ifadesi geçti: yabancı anahtar boş olabilir, SET NULL
sütunu boşaltır. Boş değerin ne anlama geldiği ise tanımlanmadı. Sıradaki ders, boş
değerin bir değer olmadığını, karşılaştırmaların onunla ne yaptığını ve doğru–yanlış
ikilisine üçüncü bir sonucun eklenmesinin sorgu yazımında hangi tuzakları açtığını gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.