Yabancı anahtar kısıtlamaları: ON UPATE ve ON DELETE ne zaman kullanılır?


196

Ben diyagramları yapabilirsiniz ve onları dönüştürür çünkü oldukça serin MySQL Workbench kullanarak benim veritabanı şeması tasarlıyorum: P

Her neyse, Yabancı Anahtar desteği nedeniyle InnoDB'yi kullanmaya karar verdim. Ancak fark ettiğim bir şey, Güncelleme ve yabancı anahtarlar için Sil seçeneklerini ayarlamanıza izin vermesidir. Birisi basit bir örnekte "Kısıtla", "Basamaklama" ve boş null değerlerinin kullanılabileceğini açıklayabilir mi?

Örneğin, a useriçeren bir tablom olduğunu varsayalım userID. Ve ben bir mesaj tablo var demek messageolan birçok çoğa (aynı birincil anahtara başvuru 2 yabancı anahtarları olan userIDiçinde usermasaya). Güncelleme ve Silme seçeneklerinin ayarlanması bu durumda yararlı mıdır? Öyleyse hangisini seçerim? Bu iyi bir örnek değilse, bunların nasıl faydalı olabileceğini göstermek için lütfen iyi bir örnek bulur musunuz?

Teşekkürler

Yanıtlar:


485

Veritabanına kısıtlamalar koymaktan çekinmeyin. Tutarlı bir veritabanına sahip olduğunuzdan emin olursunuz ve bu bir veritabanı kullanmanın iyi nedenlerinden biridir. Özellikle talep eden birkaç uygulamanız varsa (veya yalnızca bir uygulama ancak doğrudan mod ve farklı kaynaklar kullanan bir toplu iş modu ile).

MySQL ile postgreSQL'de olduğu gibi gelişmiş kısıtlamalarınız yoktur, ancak en azından yabancı anahtar kısıtlamaları oldukça ileridir.

Örnek bir örnek alalım, tez şirketindeki kişileri içeren bir kullanıcı tablosu içeren bir şirket tablosu

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

ON UPDATE deyimine bakalım :

  • GÜNCELLEŞTİRME KISITLAMASI : varsayılan : ŞİRKET tablosundaki bir şirket_kimliğini güncellemeye çalışırsanız, bir kullanıcı bu şirkete en az bağlantı verirse motor işlemi reddeder.
  • GÜNCELLEME YOK EYLEM : KISITLAMA ile aynı.
  • GÜNCELLEME KASADINDA : genellikle en iyisi : bir company_id'i tablo satırında güncellerseniz, motor bu ŞİRKETİ referans alan tüm KULLANICI satırlarında güncelleyecektir (ancak KULLANICI tablosunda tetikleyici etkinleştirilmedi, uyarı). Motor değişiklikleri sizin için takip edecek, iyi.
  • ON UPDATE SET NULL : ŞİRKET tablosundaki bir satırda company_id değerini güncellerseniz, motor ilgili USERs company_id değerini NULL olarak ayarlar (USER company_id alanında kullanılabilir olmalıdır). Bir güncellemede bununla ilgili ilginç bir şey göremiyorum, ancak yanlış olabilirim.

Ve şimdi ON DELETE tarafında:

  • KISIT SİLME : varsayılan : ŞİRKET tablosundaki bir company_id kimliğini silmeye çalışırsanız, bir kullanıcı bu şirketteki en az bağlantıdan biri hayatınızı kurtarabilirse motor işlemi reddeder.
  • EYLEM YOK SİLME : KISITLA aynı
  • KASKADI SİLME : tehlikeli : ŞİRKET tablosundaki bir şirket satırını silerseniz, motor ilgili KULLANICILAR'ı da siler. Bu tehlikelidir, ancak ikincil tablolarda otomatik temizleme yapmak için kullanılabilir (bu nedenle istediğiniz bir şey olabilir, ancak kesinlikle bir ŞİRKET <-> KULLANICI örneği için değil)
  • ON DELETE SET NULL : avuç : bir ŞİRKET satırını silerseniz ilgili KULLANICILAR otomatik olarak NULL ile ilişki kurar. Hiçbir şirket olmayan kullanıcılar için Null değerinizse, bu iyi bir davranış olabilir, örneğin, kullanıcıları bazı içeriğin yazarları olarak uygulamanızda tutmanız gerekebilir, ancak şirketi kaldırmak sizin için bir sorun değildir.

genellikle benim varsayılan: GÜNCELLEME KASKAD ÜZERİNDEN SINIRLAMAYI SİLME . bazı ON DELETE CASCADEparça tabloları için (günlükler - tüm günlükler değil -, bunun gibi şeyler) ve ON DELETE SET NULLana tablo, yabancı anahtar içeren tablo için KULLANICI tablosu için bir İŞ tablosu gibi bir 'basit özellik' olduğunda.

Düzenle

Bunu yazdığımdan beri uzun zaman oldu. Şimdi önemli bir uyarı eklemem gerektiğini düşünüyorum. MySQL, kaskadlarla ilgili büyük bir belgelenmiş sınırlamaya sahiptir. Kaskadlar tetikleyici değildir . Bu motorda tetikleyicileri kullanmak için yeterince güvendiğiniz takdirde kaskad kısıtlamalarından kaçınmalısınız.

MySQL tetikleyicileri yalnızca SQL ifadeleriyle tablolarda yapılan değişiklikler için etkinleştirilir. Görünümlerdeki değişiklikler veya SQL deyimlerini MySQL Sunucusuna iletmeyen API'ler tarafından yapılan tablolardaki değişiklikler tarafından etkinleştirilmezler.

==> Son düzenlemenin aşağısına bakın, işler bu alanda ilerliyor

Tetikleyiciler yabancı anahtar eylemleri tarafından etkinleştirilmez.

Ve bunun bir gün düzeltileceğini sanmıyorum. Yabancı anahtar kısıtlamaları InnoDb depolama alanı, Tetikleyiciler ise MySQL SQL motoru tarafından yönetilir. Her ikisi de ayrılır. Innodb, kısıtlama yönetimine sahip tek depolama alanıdır, belki bir gün doğrudan depolama motoruna tetikleyiciler eklerler, belki de değil.

Ancak, zayıf tetikleme uygulaması ile çok faydalı yabancı anahtar kısıtlamaları desteği arasında hangi öğeyi seçmeniz gerektiğine dair kendi fikrim var. Ve veritabanı tutarlılığına alıştıktan sonra PostgreSQL'i seveceksiniz.

12/2017-MySQL ile ilgili bu düzenlemeyi güncelleme:

@IstiaqueAhmed tarafından belirtildiği gibi yorumlarda durum bu konuda değişti. Bu yüzden bağlantıyı takip edin ve gerçek güncel durumu kontrol edin (gelecekte tekrar değişebilir).


8
ON DELETE CASCADE : dangerous- bir tutam tuz ile alın.
birgün14

3
Basamaklı işlemlere dikkat etmelisiniz, çok sayıda kaydın değiştirilmesi gerekiyorsa sisteminizi kilitleyebilir. Cascde delete kullanmadan önce özellikle dikkatle bakılmalıdır, genellikle çocuk kayıtları varsa silme işleminin gerçekten yapılmasını istemezsiniz. Bir müşterinin, daha önce sahip olduğu ored'lerin finansal verilerini silmek için silmesini istemezdim. Bazen, cacading'in açık olmadığından emin olmak ve kayıtları etkin olmayan olarak amrk yapmak için bir yol sağlamak daha iyidir.
HLGEM

1
İş mantığı açısından, ilginç olabilir bir durum vardır SET NULLbir in ON UPDATEbir şirket güncellenmesi Şirket> Kullanıcı ilişkisi bir müfreze temsil eder:. Örneğin: bir şirket işletme türünü değiştirirse, önceki kullanıcılar artık o işletme ile ilgili NULLolmayabilir , bu nedenle bu dizin için tercih edilebilir.
CPHPython

1
@regilero, mysql sitesine ilk bağlantınızdaki içerik ( dev.mysql.com/doc/refman/5.6/en/triggers.html ) değişmiş gibi görünüyor . This includes changes to base tables that underlie updatable viewsYapıştırdığınız şey yerine şöyle diyorThey do not activate for changes in views
Istiaque Ahmed

6
"Bir müşterinin, daha önce verdiği siparişler için finansal verileri silmek istemesini istemem." Böyle bir durumda, yine de müşterinin verilerine yine de ihtiyacınız vardır. Tasarımınız muhtemelen satırını veritabanından silmemek için müşteriyi etkin değil olarak işaretliyor olmalıdır. Pratikte, profesyonel deneyimim, aslında çok nadiren bir şey silmek istediğinizi , varsayılan olarak etkin olmayan olarak işaretlemeyi tercih ettiğiniz oldu. Kalıcı olarak silmek durumlarda ise tamam, CASCADE DELETEgenellikle de tamam bile tercih edilmektedir. Özellikle tehlikeli olarak görmüyorum.
GrandOpener

3

@MarkR yanıtına ek olarak - ORM'lere sahip birçok PHP çerçevesinin gelişmiş DB kurulumunu (yabancı anahtarlar, basamaklı silme, benzersiz kısıtlamalar) tanımaması veya kullanmaması ve beklenmedik davranışlarla sonuçlanabileceği belirtilmelidir.

Örneğin, ORM kullanarak bir kaydı DELETE CASCADEsilerseniz ve ilgili tablolardaki kayıtları silerseniz, ORM'nin bu ilgili kayıtları (genellikle otomatik) silme girişimi hataya neden olur.


11
Bu belirli ORM'yi kullanmamak için bir sebep olacaktır. Veritabanı desteğinde zayıf olan herhangi bir araç güvenilir değildir. Yabancı anahtarlar ve basamaklı silme veya güncellemeler db temelleri gelişmiş kavramlar değildir ve hiçbir yabancı veritabanı herhangi bir yabancı anahtar kısıtlaması olmadan tasarlanmamalıdır!
HLGEM

Sorun, hata atmaları. SİLMELERİ SINIRLAMAK mümkün, ancak motor hata üretmiyor, ancak anlambilimi koruyor mu? Programımın devam etmesini ve aynı zamanda diğer verilerin silinmesini engellemesini istiyorum.
TheRealChx101

2

Bunu uygulama bağlamında dikkate almanız gerekir. Genel olarak, bir veritabanı değil, bir uygulama tasarlamanız gerekir (veritabanı uygulamanın sadece bir parçasıdır).

Başvurunuzun çeşitli durumlara nasıl yanıt vermesi gerektiğini düşünün.

Varsayılan eylem, aptal programlama hatalarını önlediği için normalde istediğiniz şey olan işlemi kısıtlamaktır (yani izin vermemek). Bununla birlikte, DELETE CASCADE'de de yararlı olabilir. Bu gerçekten uygulamanıza ve belirli nesneleri nasıl silmek istediğinize bağlıdır.

Şahsen, InnoDB kullanacağım çünkü verilerinizi çöpe atmaz (cf MyISAM, yapar), FK kısıtlamaları olduğu için değil.

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.