Mysql
 sql >> Database >  >> RDS >> Mysql

In SQL, va bene che due tabelle facciano riferimento l'una all'altra?

No, non va bene. I riferimenti circolari tra le tabelle sono disordinati. Vedi questo articolo (vecchio di decenni):SQL By Design:The Circular Reference

Alcuni DBMS possono gestirli, e con particolare attenzione, ma MySQL avrà problemi.

Opzione 1

Come tuo progetto, per rendere nullable uno dei due FK. Questo ti permette di risolvere il problema dell'uovo e della gallina (in quale tabella devo prima inserire?).

C'è un problema però con il tuo codice. Consentirà a un prodotto di avere un'immagine predefinita in cui quell'immagine farà riferimento a un altro prodotto!

Per non consentire un tale errore, il tuo vincolo FK dovrebbe essere:

CONSTRAINT FK_products_1 
  FOREIGN KEY (id, default_picture_id) 
  REFERENCES products_pictures (product_id, id)
  ON DELETE RESTRICT                            --- the SET NULL options would 
  ON UPDATE RESTRICT                            --- lead to other issues

Ciò richiederà un UNIQUE vincolo/indice nella tabella products_pictures su (product_id, id) affinché l'FK di cui sopra sia definito e funzioni correttamente.

Opzione 2

Un altro approccio consiste nel rimuovere il Default_Picture_ID colonna forma il product tabella e aggiungi un IsDefault BIT colonna nella picture tavolo. Il problema con questa soluzione è come consentire a una sola immagine per prodotto di avere quel bit e a tutti gli altri di averlo spento. In SQL-Server (e penso in Postgres) questo può essere fatto con un indice parziale:

CREATE UNIQUE INDEX is_DefaultPicture 
  ON products_pictures (Product_ID)
  WHERE IsDefault = 1 ;

Ma MySQL non ha tale funzionalità.

Opzione 3

Questo approccio ti consente anche di avere entrambe le colonne FK definite come NOT NULL consiste nell'utilizzare vincoli differibili. Funziona in PostgreSQL e penso in Oracle. Controlla questa domanda e la risposta di @Erwin:Vincolo di chiave esterna complesso in SQLAlchemy (le Tutte le colonne chiave NON NULL parte).

I vincoli in MySQL non possono essere rinviati.

Opzione 4

L'approccio (che trovo più pulito) è rimuovere il Default_Picture_ID colonna e aggiungi un'altra tabella. Nessun percorso circolare nei vincoli FK e tutte le colonne FK saranno NOT NULL con questa soluzione:

product_default_picture
----------------------
product_id          NOT NULL
default_picture_id  NOT NULL
PRIMARY KEY (product_id)
FOREIGN KEY (product_id, default_picture_id)
  REFERENCES products_pictures (product_id, id)

Ciò richiederà anche un UNIQUE vincolo/indice nella tabella products_pictures su (product_id, id) come nella soluzione 1.

Per riassumere, con MySQL hai due opzioni:

  • opzione 1 (una colonna FK nullable) con la correzione sopra per applicare correttamente l'integrità

  • opzione 4 (nessuna colonna FK nullable)