SQL'deki toplam katılım kısıtlamaları ile çoktan çoğa ilişkinin uygulanması


17

Aşağıdaki Varlık-İlişki diyagramında gösterilen senaryoyu SQL'de nasıl uygulamalıyım?

Toplam katılım kısıtlamaları ile çoktan çoğa ilişki

Gösterildiği gibi, her Avarlık türü oluşumu, en az bir B muadili (çift bağlantı çizgileriyle gösterilir) ve tersi ile ilişkili olmalıdır . Aşağıdaki üç tablo oluşturmak gerektiğini biliyorum:

    CREATE TABLE A
    (
        a INT NOT NULL,
        CONSTRAINT A_PK PRIMARY KEY (a)
    );

    CREATE TABLE B
    (
        b INT NOT NULL,
        CONSTRAINT B_PK PRIMARY KEY (b)
    );

    CREATE TABLE R
    (
        a INT NOT NULL,
        b INT NOT NULL,
        CONSTRAINT R_PK      PRIMARY KEY (a, b),
        CONSTRAINT R_to_A_FK FOREIGN KEY (a)
            REFERENCES A (a),
        CONSTRAINT R_to_B_FK FOREIGN KEY (b)
            REFERENCES B (b)
    );

Peki, toplam katılım kısıtlamalarının uygulanmasına ne dersiniz (yani, her birinin ya da birinin her bir örneğinin diğeriyle en az bir ilişkide yer almasını zorunlu kılmak )?AB

Yanıtlar:


16

SQL'de yapmak kolay değil ama imkansız değil. Bunun yalnızca DDL aracılığıyla uygulanmasını istiyorsanız, DBMS'nin DEFERRABLEkısıtlamaları uygulaması gerekir . Bu yapılabilir (ve bunları uygulayan Postgres'te çalışmak için kontrol edilebilir):

-- lets create first the 2 tables, A and B:
CREATE TABLE a 
( aid INT NOT NULL,
  bid INT NOT NULL,
  CONSTRAINT a_pk PRIMARY KEY (aid) 
 );

CREATE TABLE b 
( bid INT NOT NULL,
  aid INT NOT NULL,
  CONSTRAINT b_pk PRIMARY KEY (bid) 
 );

-- then table R:
CREATE TABLE r 
( aid INT NOT NULL,
  bid INT NOT NULL,
  CONSTRAINT r_pk PRIMARY KEY (aid, bid),
  CONSTRAINT a_r_fk FOREIGN KEY (aid) REFERENCES a,  
  CONSTRAINT b_r_fk FOREIGN KEY (bid) REFERENCES b
 );

Buraya kadar her "normal" tasarım vardır Asıfır, bir çoğu ya bağlı olabilir Bve her Bsıfır, bir ya da birçok ilişkili olabilir A.

"Toplam katılım" kısıtlamasının ters sırada ( sırasıyla referans olarak Ave Breferans olarak R) kısıtlamalara ihtiyacı vardır . FOREIGN KEYZıt yönlerde (X'den Y'ye ve Y'den X'a) kısıtlamalara sahip olmak bir daire (bir "tavuk ve yumurta" sorunu) oluşturuyor ve bu yüzden en azından bunlardan birine ihtiyacımız var DEFERRABLE. Bu durumda iki çemberimiz var ( A -> R -> Ave B -> R -> Bbu nedenle iki ertelenebilir kısıtlamaya ihtiyacımız var:

-- then we add the 2 constraints that enforce the "total participation":
ALTER TABLE a
  ADD CONSTRAINT r_a_fk FOREIGN KEY (aid, bid) REFERENCES r 
    DEFERRABLE INITIALLY DEFERRED ;

ALTER TABLE b
  ADD CONSTRAINT r_b_fk FOREIGN KEY (aid, bid) REFERENCES r 
    DEFERRABLE INITIALLY DEFERRED ;

Sonra veri ekleyebileceğimizi test edebiliriz. INITIALLY DEFERREDGerekli olmadığını unutmayın . Kısıtlamaları şu şekilde tanımlayabilirdik, DEFERRABLE INITIALLY IMMEDIATEancak SET CONSTRAINTSişlem sırasında bunları ertelemek için ifadeyi kullanmamız gerekirdi . Yine de her durumda, tablolara tek bir işlemde eklememiz gerekir:

-- insert data 
BEGIN TRANSACTION ;
    INSERT INTO a (aid, bid)
    VALUES
      (1, 1),    (2, 5),
      (3, 7),    (4, 1) ;

    INSERT INTO b (aid, bid)
    VALUES
      (1, 1),    (1, 2),
      (2, 3),    (2, 4),
      (2, 5),    (3, 6),
      (3, 7) ;

    INSERT INTO r (aid, bid)
    VALUES
      (1, 1),    (1, 2),
      (2, 3),    (2, 4),
      (2, 5),    (3, 6),
      (3, 7),    (4, 1),
      (4, 2),    (4, 7) ; 
 END ;

SQLfiddle'da test edildi .


DBMS'de DEFERRABLEkısıtlamalar yoksa , bir geçici çözüm A (bid)ve B (aid)sütunları olarak tanımlamaktır NULL. INSERTProsedürler / tablolar daha sonra birinci içine inserte sahip olacak Ave B(boş değerlere koyarak bidve aiddaha sonra, sırasıyla) içine girecek Rve daha sonra, ilgili boş olmayan değerlere üzerinde boş değerleri güncelleştirmek R.

Bu yaklaşımla, DBMS DDL tarafından gereksinimleri yalnız ama her zorlamaz INSERT(ve UPDATEve DELETEve MERGEprosedür olarak kabul edilir ve buna göre ayarlanır ve kullanıcılar sadece onları değil tablolarına direkt yazma erişimine sahip kullanmaya kısıtlı olmak zorunda zorundadır).

FOREIGN KEYKısıtlamalarda çemberlere sahip olmak birçok iyi uygulama tarafından kabul edilmez ve iyi nedenlerle karmaşıklık bunlardan biridir. Örneğin ikinci yaklaşımla (boş değerli sütunlarla), DBMS'ye bağlı olarak satırların güncellenmesi ve silinmesinin yine de ekstra kodla yapılması gerekecektir. Örneğin SQL Server'da ON DELETE CASCADE, FK çevreleri olduğunda basamaklı güncellemelere ve silme işlemlerine izin verilmediği için yalnızca koyamazsınız .

Lütfen bu ilgili sorunun cevaplarını da okuyun:
Ayrıcalıklı bir çocukla birebir ilişkiniz nasıl olur?


Başka bir 3. yaklaşım (yukarıda belirtilen soruya cevabım bakın) dairesel FK'ları tamamen kaldırmaktır. Yani, kodun ilk kısmını tutarak (tablolarla A, B, Rsadece R A ve B ve yabancı tuşları) (aslında basitleştirilmesi) neredeyse bozulmamış, biz başka tablo eklemek Agelen "olması gereken bir" ilgili öğeyi saklamak için B. Böylece, A (bid)sütun A_one (bid)B'ye hareket eder. B'den A'ya ters ilişki için aynı işlem yapılır:

CREATE TABLE a 
( aid INT NOT NULL,
  CONSTRAINT a_pk PRIMARY KEY (aid) 
 );

CREATE TABLE b 
( bid INT NOT NULL,
  CONSTRAINT b_pk PRIMARY KEY (bid) 
 );

-- then table R:
CREATE TABLE r 
( aid INT NOT NULL,
  bid INT NOT NULL,
  CONSTRAINT r_pk PRIMARY KEY (aid, bid),
  CONSTRAINT a_r_fk FOREIGN KEY (aid) REFERENCES a,  
  CONSTRAINT b_r_fk FOREIGN KEY (bid) REFERENCES b
 );

CREATE TABLE a_one 
( aid INT NOT NULL,
  bid INT NOT NULL,
  CONSTRAINT a_one_pk PRIMARY KEY (aid),
  CONSTRAINT r_a_fk FOREIGN KEY (aid, bid) REFERENCES r
 );

CREATE TABLE b_one
( bid INT NOT NULL,
  aid INT NOT NULL,
  CONSTRAINT b_one_pk PRIMARY KEY (bid),
  CONSTRAINT r_b_fk FOREIGN KEY (aid, bid) REFERENCES r
 );

1. ve 2. yaklaşım arasındaki fark, dairesel FK'ler olmamasıdır, bu nedenle basamaklı güncellemeler ve silme işlemleri iyi çalışır. "Toplam katılımın" uygulanması, 2. yaklaşımda olduğu gibi yalnızca DDL tarafından gerçekleştirilmez ve uygun prosedürlerle ( INSERT/UPDATE/DELETE/MERGE) yapılmalıdır . 2. yaklaşımla küçük bir fark, tüm sütunların boş bırakılamaz olarak tanımlanabilmesidir.


Başka bir 4. yaklaşım ( yukarıda belirtilen sorudaki @Aaron Bertrand'ın cevabına bakınız), DBMS'nizde mevcutsa filtrelenmiş / kısmi benzersiz dizinler kullanmaktır ( Rbu durumda, bunlardan ikisine ihtiyacınız olacak ). Bu, 2 yaklaşıma çok benzer, ancak 2 ekstra tabloya ihtiyacınız yoktur. "Toplam katılım" kısıtlaması hala kodla uygulanmalıdır.


4. yaklaşım (biraz gizli) aslında mükemmel. Örnek olarak postgresql.org/docs/9.6/static/indexes-partial.html Postgres için Örnek 11-3'e bakınız .
Danilo

@ Dailo Maksimum 1 toplam katılım olmasını sağlamanın mükemmel olduğunu görüyorum (bazı ek alanlara dayalı - postgre örneğinde başarı). En az bir başarı - bu konudaki asıl soru olduğundan emin olmak için nasıl yardımcı olduğunu göremiyorum. Lütfen biraz açıklayabilir misiniz?
Alexander Mihailov

3

Doğrudan yapamazsın. Yeni başlayanlar için, zaten bir B olmadan A için kayıt ekleyemezsiniz, ancak A kaydı yoksa B kaydını oluşturamazsınız. Tetikleyiciler gibi şeyleri kullanarak bunu uygulamanın birkaç yolu vardır - her eki kontrol etmeniz ve AB bağlantı tablosunda karşılık gelen en az bir kaydın kaldığını silmeniz 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.