tags:

views:

1328

answers:

4

What's the best way to create a non-NULL constraint in MySQL such that fieldA and fieldB can't both be NULL. I don't care if either one is NULL by itself, just as long as the other field has a non-NULL value. And if they both have non-NULL values, then it's even better.

+1  A: 

I've done something similar in SQL Server, I'm not sure if it will work directly in MySQL, but:

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

At least I believe that's the syntax.

However, keep in mind that you cannot create check constraints across tables, you can only check the columns within one table.

Miquella
+2  A: 

This is the standard syntax for such a constraint, but MySQL blissfully ignores the constraint afterwards

ALTER TABLE `generic` 
ADD CONSTRAINT myConstraint 
CHECK (
  `FieldA` IS NOT NULL OR 
  `FieldB` IS NOT NULL
)
Sklivvz
Since this is the standard SQL syntax, this should work with other SQL-based databases also. (It definitely works for PostgreSQL.)
Neall
The MySQL manual says: "The CHECK clause is parsed but ignored by all storage engines." I tried it and it's true.
Bill Karwin
It's even ignored for InnoDB tables? That sucks.
Neall
Edited correspondingly. Please remove the downvote if you think the answer is now useful. Thanks,
Sklivvz
I would have removed the downvote, but SO now tells me "this vote is too old to be undone or changed." Besides, although your answer is now truthful, it's not useful inasmuch as it doesn't solve the question asked by mpeters. :-)
Bill Karwin
:) Yeah, but at least it points out a gotcha in MySQL... Don't worry about the down vote I've got rep to spare... ;) The answer is bubbling up anyways, so at least people will be warned.
Sklivvz
+6  A: 

@Sklivvz: Testing with MySQL 5.0.51a, I find it parses a CHECK constraint, but does not enforce it. I can insert (NULL, NULL) with no error. Tested both MyISAM and InnoDB. Subsequently using SHOW CREATE TABLE shows that a CHECK constraint is not in the table definition, even though no error was given when I defined the table.

This matches the MySQL manual which says: "The CHECK clause is parsed but ignored by all storage engines."

So for MySQL, you would have to use a trigger to enforce this rule. The only problem is that MySQL triggers have no way of raising an error or aborting an INSERT operation. One thing you can do in the trigger to cause an error is to set a NOT NULL column to 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

You also need a similar trigger BEFORE UPDATE.

Bill Karwin
Bill, fixed my answer accordingly, thanks,
Sklivvz
Instead of using a `NOT NULL` column to force an error, you can do something like `SET NEW.FieldA = 1/0` which will throw a division-by-zero error. Not a very *useful* error message, but at least you don't have to add special columns. I really wish MySQL supported proper `CHECK` constraints, or at least had a better way to throw errors from triggers. :(
friedo
@friedo: Since the OP's question was about enforcing some special not-null conditions, I thought a null violation error message would be appropriate. Another trick you can do in a MySQL trigger is declare an integer variable in the trigger and try to assign a string to it. It raises an error message that includes the string you use. :)
Bill Karwin
+4  A: 

This isn't an answer directly to your question, but some additional information.

When dealing with multiple columns and checking if all are null or one is not null, I typically use COALESCE() - it's brief, readable and easily maintainable if the list grows:

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

This can be used in your trigger.

Cade Roux