Le colonne della tabella con una chiave esterna possono essere NULL?


235

Ho una tabella che ha diverse colonne ID per altre tabelle.

Voglio una chiave esterna per forzare l'integrità solo se inserisco i dati. Se faccio un aggiornamento in un secondo momento per popolare quella colonna, allora dovrebbe anche controllare il vincolo.

(Probabilmente dipende dal server di database, sto usando il tipo di tabella MySQL e InnoDB)

Credo che questa sia un'aspettativa ragionevole, ma correggimi se sbaglio.


6
Non conosco MySQL, ma MS SQL Server consente alle chiavi esterne di essere annullabili con la semantica desiderata. Mi aspetto che sia un comportamento standard.
Jeffrey L Whitledge,

1
la chiave esterna, non può essere nulla per impostazione predefinita in mySQL, il motivo è semplice, se fai riferimento a qualcosa e lo lasci null, perderai l'integrità dei dati. quando si crea il set di tabelle, consentire null su NOT e quindi applicare il vincolo di chiave esterna. Non è possibile impostare null sull'aggiornamento, è necessario inviare un errore, ma è possibile (è necessario) semplicemente non aggiornare questa colonna e aggiornare solo i campi che è necessario modificare.
JoelBonetR,

Risposte:


245

Sì, è possibile applicare il vincolo solo quando il valore non è NULL. Questo può essere facilmente testato con il seguente esempio:

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                    parent_id INT NULL,
                    FOREIGN KEY (parent_id) REFERENCES parent(id)
) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)


INSERT INTO child (id, parent_id) VALUES (2, 1);

-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key 
-- constraint fails (`t/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
-- (`parent_id`) REFERENCES `parent` (`id`))

Il primo inserimento passerà perché inseriamo un NULL nel file parent_id. Il secondo inserimento non riesce a causa del vincolo di chiave esterna, poiché abbiamo cercato di inserire un valore che non esiste nella parenttabella.


16
La tabella padre può anche essere dichiarata con ID INT NOT NULL.
Sarà il

@CJDennis Se lo fai in modo che solo una riga possa avere un ID null, potrebbe essere utilizzata come valori di fallback per altre righe. (Anche se potrebbe funzionare meglio per il DB se si utilizzano solo più colonne.) Il vincolo predefinito sembra un problema se si desidera sapere in seguito se un valore è stato originariamente impostato come "predefinito" (utilizzando null) o impostato su un valore che risulta essere uguale a "predefinito". Avendo una riga con un ID null, è possibile indicare chiaramente che questa riga non deve essere utilizzata come una riga normale e può utilizzare la riga come un modo per fornire una sorta di valore predefinito dinamico per altre righe.
Ouroborus,

1
penso che la parent_id INT NULLparte sia (verbosamente) uguale aparent_id int default null

Nota a margine per gli utenti java, se usi ibatis o altri ORM e utilizzi la primitiva intinvece che Integernei membri della tua classe, il valore predefinito non sarà mai nullo, ma sarà 0 e fallirai il vincolo.
Jim Ford,

32

Ho scoperto che durante l'inserimento, i valori della colonna null dovevano essere dichiarati specificamente come NULL, altrimenti avrei avuto un errore di violazione del vincolo (al contrario di una stringa vuota).


8
Non è possibile impostare un valore predefinito di NULL sulla colonna per consentire ciò?
Kevin Coulombe,

Sì, nella maggior parte delle lingue NULL è diverso da una stringa vuota. Forse sottile all'inizio, ma fondamentale da ricordare.
Gary,

Ehi Backslider, dici "(al contrario di una stringa vuota)", ma non penso che volevi dire che INSERIRI un valore di stringa vuota, ma piuttosto che non specifichi un valore per per il valore a tutti? cioè non hai nemmeno menzionato la colonna nella tua INSERT INTO {table} {list_of_columns}? Perché è vero per me; omettere la menzione della colonna provoca errori, ma l'inclusione e l'impostazione esplicita di NULL risolve l'errore. Se ho ragione, penso che il commento di @ Gary non si applichi (perché non intendevi una stringa vuota), ma quello di @Kevin Coulombe potrebbe essere utile ...
The Red Pea,

Sì, il suggerimento di @ KevinCoulombe funziona, ho descritto come ottenere questo risultato con gli script di migrazione di Entity Framework Core, qui
The Red Pea,

È importante sottolineare che la logica per essere espliciti quando si aggiorna un record contenente chiavi esterne NULL si applica solo ai tipi di stringa (varchar, ecc.), Poiché altrimenti una stringa vuota può essere passata come predefinita. Questo è il caso di MySQL e si verifica un errore di integrità durante l'aggiornamento.
CodeMantle

4

Sì, funzionerà come previsto. Sfortunatamente, mi sembra di avere difficoltà a trovare una dichiarazione esplicita di ciò nel manuale di MySQL .

Le chiavi esterne indicano che il valore deve esistere nell'altra tabella. NULLO riferisce all'assenza di valore, quindi quando si imposta una colonna su NULL, non avrebbe senso provare a imporre vincoli su questo.


Per impostazione predefinita, la chiave esterna deve fare riferimento a una chiave (primaria) che non è NULL, ma durante la fase di sviluppo quando è necessario inserire prima i dati multipli nella tabella figlio, a cui non sappiamo a chi fare riferimento (la tabella padre) . Questo è il motivo per cui è consentito il valore NULL. Nella produzione avere NULL sarà un flusso di progettazione, che si può dire più o meno.
Vimal Krishna,

2

Quanto sopra funziona, ma non è così. Nota ON ELIMINA CASCATA

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                 PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                parent_id INT NULL,
                FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE

) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)

4
Cosa intendi con "quanto sopra"? Nota che se ti riferisci a un'altra risposta, l'ordine potrebbe cambiare.
d219

2

Sì, il valore può essere NULL, ma devi essere esplicito. Ho già sperimentato la stessa situazione in precedenza, ed è facile dimenticare PERCHE 'questo accade, e quindi ci vuole un po' di tempo per ricordare cosa deve essere fatto.

Se i dati inviati vengono trasmessi o interpretati come una stringa vuota, falliranno. Tuttavia, impostando esplicitamente il valore su NULL durante INSERTING o UPDATING, sei a posto.

Ma questo è il divertimento della programmazione, no? Creare i nostri problemi e poi risolverli! Saluti!


1

Un altro modo per aggirare questo sarebbe inserire un elemento DEFAULT nell'altra tabella. Ad esempio, qualsiasi riferimento a uuid = 00000000-0000-0000-0000-000000000000 sull'altra tabella indicherebbe nessuna azione. È inoltre necessario impostare tutti i valori affinché quell'id sia "neutro", ad esempio 0, stringa vuota, null per non influire sulla logica del codice.


2
Questa non è la stessa cosa Un valore predefinito o "neutro" non è uguale a NULL, l'assenza di un valore. Senza discutere il merito di un valore predefinito su un valore NULL, il tuo fraseggio è un misto misto. "Un altro modo per aggirare questo sarebbe quello di inserire un elemento null nell'altra tabella" dovrebbe dire qualcosa di più simile a "Un altro modo per aggirare questo sarebbe inserire un elemento DEFAULT nell'altra tabella"
blindguy

0

Ho anche bloccato su questo problema. Ma ho risolto semplicemente definendo la chiave esterna come unsigned integer. Trova l'esempio seguente-

CREATE TABLE parent (
   id int(10) UNSIGNED NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id int(10) UNSIGNED NOT NULL,
    parent_id int(10) UNSIGNED DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.