web-dev-qa-db-fra.com

Les colonnes de table avec une clé étrangère peuvent-elles être NULL?

J'ai une table qui a plusieurs colonnes d'ID à d'autres tables.

Je veux une clé étrangère pour forcer l'intégrité seulement si j'y mets des données. Si je fais une mise à jour ultérieurement pour remplir cette colonne, il devrait également vérifier la contrainte.

(Cela dépend probablement du serveur de base de données, j'utilise le type de table MySQL & InnoDB)

Je crois que c'est une attente raisonnable, mais corrigez-moi si je me trompe.

194
bender

Oui, vous pouvez appliquer la contrainte uniquement lorsque la valeur n'est pas NULL. Ceci peut être facilement testé avec l'exemple suivant:

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`))

La première insertion passera parce que nous insérons un NULL dans le parent_id. La deuxième insertion échoue à cause de la contrainte de clé étrangère, car nous avons essayé d'insérer une valeur qui n'existe pas dans la table parent.

199
Daniel Vassallo

J'ai constaté que lors de l'insertion, les valeurs de colonne nulles devaient être spécifiquement déclarées comme NULL, sinon j'obtiendrais une erreur de violation de contrainte (par opposition à une chaîne vide).

25
Backslider

Oui, cela fonctionnera comme prévu. Malheureusement, il semble que j’ai du mal à trouver une déclaration explicite à ce sujet dans manuel MySQL .

Les clés étrangères signifient que la valeur doit exister dans l'autre table. NULL fait référence à l'absence de valeur. Ainsi, lorsque vous définissez une colonne sur NULL, cela n'a aucun sens d'essayer d'appliquer des contraintes sur cela.

3
davidtbernal

Ce qui précède fonctionne mais cela ne fonctionne pas. Notez le casse-tête ON DELETE

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)
2
MrFabulous

Une autre solution consiste à insérer un élément DEFAULT dans l'autre table. Par exemple, toute référence à uuid = 00000000-0000-0000-0000-000000000000 sur l'autre table n'indiquerait aucune action. Vous devez également définir toutes les valeurs pour que cet identifiant soit "neutre", par exemple. 0, chaîne vide, null afin de ne pas affecter votre logique de code.

1
Wildhammer

J'ai aussi collé sur cette question. Mais j'ai résolu simplement en définissant la clé étrangère comme unsigned integer. Trouvez l'exemple ci-dessous

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;
0
Shams Reza