2008-10-04 7 views
11

Quelle est la meilleure façon de créer une contrainte non NULL dans MySQL tels que FieldA et FieldB ne peuvent pas être à la fois NULL. Peu importe si l'un est NULL par lui-même, aussi longtemps que l'autre champ a une valeur non-NULL. Et si les deux ont des valeurs non NULL, alors c'est encore mieux.Soit ou des contraintes non nulles dans MySQL

Répondre

4

MySQL 5.5 introduit SIGNAL, donc on n'a pas besoin de la colonne supplémentaire dans la réponse de Bill Karwin plus. Bill a souligné que vous avez également besoin d'un déclencheur pour la mise à jour, donc j'ai inclus cela aussi.

CREATE TABLE foo (
    FieldA INT, 
    FieldB INT 
); 

DELIMITER // 
CREATE TRIGGER InsertFieldABNotNull BEFORE INSERT ON foo 
FOR EACH ROW BEGIN 
    IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN 
    SIGNAL SQLSTATE '45000' 
    SET MESSAGE_TEXT = '\'FieldA\' and \'FieldB\' cannot both be null'; 
    END IF; 
END// 
CREATE TRIGGER UpdateFieldABNotNull BEFORE UPDATE ON foo 
FOR EACH ROW BEGIN 
    IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN 
    SIGNAL SQLSTATE '45000' 
    SET MESSAGE_TEXT = '\'FieldA\' and \'FieldB\' cannot both be null'; 
    END IF; 
END// 
DELIMITER ; 

INSERT INTO foo (FieldA, FieldB) VALUES (NULL, 10); -- OK 
INSERT INTO foo (FieldA, FieldB) VALUES (10, NULL); -- OK 
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, NULL); -- gives error 
UPDATE foo SET FieldA = NULL; -- gives error 
2

Je l'ai fait quelque chose de similaire dans SQL Server, je ne sais pas si cela va fonctionner directement dans MySQL, mais:

ALTER TABLE tableName ADD CONSTRAINT constraintName CHECK ((fieldA IS NOT NULL) OR (fieldB IS NOT NULL)); 

Au moins, je crois que c'est la syntaxe.

Cependant, gardez à l'esprit que vous ne pouvez pas créer des contraintes de contrôle sur les tables, vous ne pouvez vérifier les colonnes d'une table.

4

Ceci est la syntaxe standard pour une telle contrainte, mais MySQL ignore béatement la contrainte après

ALTER TABLE `generic` 
ADD CONSTRAINT myConstraint 
CHECK (
    `FieldA` IS NOT NULL OR 
    `FieldB` IS NOT NULL 
) 
+0

Étant donné qu'il s'agit de la syntaxe SQL standard, cela devrait également fonctionner avec d'autres bases de données SQL. (Cela fonctionne définitivement pour PostgreSQL.) – Neall

+0

Le manuel MySQL dit: "La clause CHECK est analysée mais ignorée par tous les moteurs de stockage." Je l'ai essayé et c'est vrai. –

+0

Il est même ignoré pour les tables InnoDB? C'est nul. – Neall

7

@Sklivvz: Test avec MySQL 5.0.51a, je trouve une analyse contrainte CHECK, mais ne l'applique pas il. Je peux insérer (NULL, NULL) sans erreur. Testé à la fois MyISAM et InnoDB. Par la suite en utilisant SHOW CREATE TABLE montre qu'une contrainte CHECK n'est pas dans la définition de table, même si aucune erreur n'a été donnée lorsque j'ai défini la table.

Cela correspond à la MySQL manual qui dit: « La clause CHECK est analysée mais ignorée par tous les moteurs de stockage. »

Donc, pour MySQL, vous devez utiliser un déclencheur pour faire respecter cette règle. Le seul problème est que les déclencheurs MySQL n'ont aucun moyen de déclencher une erreur ou d'interrompre une opération INSERT. Une chose que vous pouvez faire dans le déclencheur pour provoquer une erreur est de définir une colonne NOT NULL sur NULL.

CREATE TABLE foo (
    FieldA INT, 
    FieldB INT, 
    FieldA_or_FieldB TINYINT NOT NULL; 
); 

DELIMITER // 
CREATE TRIGGER FieldABNotNull BEFORE INSERT ON foo 
FOR EACH ROW BEGIN 
    IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN 
    SET NEW.FieldA_or_FieldB = NULL; 
    ELSE 
    SET NEW.FieldA_or_FieldB = 1; 
    END IF; 
END// 

INSERT INTO foo (FieldA, FieldB) VALUES (NULL, 10); -- OK 
INSERT INTO foo (FieldA, FieldB) VALUES (10, NULL); -- OK 
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, NULL); -- gives error 

Vous avez également besoin d'un déclencheur similaire AVANT LA MISE À JOUR.

+0

Bill, corrigé ma réponse en conséquence, merci, – Sklivvz

+0

Au lieu d'utiliser une colonne 'NOT NULL' pour forcer une erreur, vous pouvez faire quelque chose comme' SET NEW.FieldA = 1/0' qui va lancer une erreur de division par zéro . Pas un message d'erreur très * utile *, mais au moins vous n'avez pas besoin d'ajouter des colonnes spéciales. Je souhaite vraiment que MySQL supporte correctement les contraintes 'CHECK', ou au moins ait un meilleur moyen de lancer des erreurs à partir des déclencheurs. :( – friedo

+0

@friedo: Comme la question de l'OP consistait à imposer des conditions spéciales non nulles, je pensais qu'un message d'erreur null violation serait approprié.Une autre astuce que vous pouvez faire dans un déclencheur MySQL est de déclarer une variable entière dans le déclencheur et essayer pour lui assigner une chaîne, elle déclenche un message d'erreur qui inclut la chaîne que vous utilisez. :) –

7

Ce n'est pas une réponse directement à votre question, mais quelques informations supplémentaires.

Lorsque vous traitez avec plusieurs colonnes et de vérifier si tous sont nuls ou on est non nul, j'utilise généralement COALESCE() - il est bref, lisible et facilement maintenable si la liste se développe:

COALESCE(a, b, c, d) IS NULL -- True if all are NULL 

COALESCE(a, b, c, d) IS NOT NULL -- True if any one is not null 

Cela peut être utilisé dans votre déclencheur.

Questions connexes