34


I am curious to know is it possible to create a conditional not null constraint in sql? In otherwords is it possible to create a constraint such that a column B can be null as long column A contains lets say 'NEW' but if the contents of column A changes to something else then column B is no longer allowed to be null?
And to extend on that, it is then possible to make it so that column B must be null or empty as long as column A says 'NEW'?
Thanks All :D

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
Heinrich
  • 2,144
  • 3
  • 23
  • 39
  • FWIW, I don't think most database designers would call this a conditional constraint. *I'd* probably just call it a constraint, but if I had to qualify it more, I might call it a multi-column constraint or a multi-column CHECK() constraint. It's standard SQL. – Mike Sherrill 'Cat Recall' Apr 23 '12 at 02:25
  • @Catcall: Chris Date quote: "such constraints are sometimes referred to, informally, as tuple constraints, or row constraints in SQL—though this latter term is also used in SQL to mean, more specifically, a row constraint that can't be formulated as a column constraint... All such usages are deprecated, however, because constraints constrain updates and... there's no such thing as a tuple or row level update in the relational world." – onedaywhen Apr 23 '12 at 09:13
  • @onedaywhen: I don't have the current edition. In the 7th edition, what I'm seeing here (apart from NULL) looks like what he calls a relvar constraint (p253). It accommodates multiple columns, and he says it can be arbitrarily complex (meaning, I suppose, that it could refer to other relations, views, aggregates, and phases of the moon). – Mike Sherrill 'Cat Recall' Apr 23 '12 at 15:23
  • @Catcall: [SQL and Relational Theory: How to Write Accurate SQL Code](http://books.google.co.uk/books?id=WuZGD5tBfMwC&pg=PA174&lpg=PA174&dq=%22a+row+constraint+that+can't+be+formulated+as+a+column+constraint%22&source=bl&ots=WepZ-rBKHD&sig=Za4O9W5q6mZ1BQsaoCdLx5J6jmQ&hl=en&sa=X&ei=mVKWT4aSA8mW-wbYhPT9DQ&ved=0CCUQ6AEwAA#v=onepage&q=%22a%20row%20constraint%20that%20can't%20be%20formulated%20as%20a%20column%20constraint%22&f=false) – onedaywhen Apr 24 '12 at 07:15
  • In that paragraph, he also says, "Note in particular that it can be checked for a given supplier tuple by examining just that tuple in isolation--there's no need to look at any other tuples in the relvar or any other relvars in the database." (Sounds like a good thing.) I *think* "all such usages" refers to "a row constraint that can't be formulated as a column constraint", not to a column constraint that refers to other columns. But I could be wrong. – Mike Sherrill 'Cat Recall' Apr 24 '12 at 12:09

4 Answers4

43

This is perfectly fine for CONSTRAINT CHECK. Just do this:

Requirement:

is it possible to create a constraint such that a column B can be null as long column A contains lets say 'NEW' but if the contents of column A changes to something else then column B is no longer allowed to be null?

Note the phrase: column B can be null

Solution:

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      A = 'NEW' -- B can be null or not null: no need to add AND here
      OR (A <> 'NEW' AND B IS NOT NULL)
    )
);

You can simplify it further:

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      A = 'NEW' 
      OR B IS NOT NULL
    )
);

Requirement mutually incompatible to requirement above:

And to extend on that, it is then possible to make it so that column B must be null or empty as long as column A says 'NEW'?

Note the phrase: column B must be null

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      (A = 'NEW' AND B IS NULL)
      OR A <> 'NEW'
    )
);

Could be simplified with this, simpler but might not be as readable as above though:

create table tbl
(
    A varchar(10) not null,
    B varchar(10),

    constraint uk_tbl check
    (
      A <> 'NEW'
      OR B IS NULL
    )
);
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • 3
    I believe the only modern SQL dbms this *won't* work on is MySQL. MySQL doesn't enforce CHECK() constraints. (Question is tagged "SQL", so a comment seemed relevant.) – Mike Sherrill 'Cat Recall' Apr 23 '12 at 02:11
  • I just infer he's using Sql Server, I checked some of his questions. Most database questions aren't tagged specifically, and oughta be. If possible, I want stackoverflow not to accept sql tag, so everyone could be more specific on what RDBMS they are using – Michael Buen Apr 23 '12 at 02:15
  • That constraint check is already working on Sql Server since day 1, likewise on Postgresql. I tested that DDL on both SQL Server and Postgresql, both of them accept it – Michael Buen Apr 23 '12 at 02:17
  • Re your first SQL statement: the definition for `uk_tbl` is not a valid expression. – onedaywhen Apr 23 '12 at 08:03
  • ...but otherwise agree your 'simpler' rewritten constraints, so +1 (see my answer for a different approach to the same answer :) – onedaywhen Apr 23 '12 at 08:14
  • @Catcall: I mistakenly deleted the `OR` on the first check constraint when I removed superfluous parenthesis. Corrected it now. Thanks – Michael Buen Apr 23 '12 at 10:44
  • If one day, you encounter a need for allowing *multiple nulls* (ANSI SQL-compliant) on a unique column in SQL Server, this might come in handy: http://www.ienablemuch.com/2010/12/postgresql-said-sql-server2008-said-non.html – Michael Buen Apr 23 '12 at 12:28
2

I think your first stated requirement is:

IF ( B IS NULL ) THEN ( A = 'NEW' )

Apply the implication rewrite rule:

IF ( X ) THEN ( Y )   <=>   ( NOT ( X ) OR ( Y ) )

In your case;

( NOT ( B IS NULL ) OR ( A = 'NEW' ) )

Minor rewrite to take advantage of SQL syntax:

( B IS NOT NULL OR A = 'NEW' )

Your second stated ("extend") requirement:

IF ( A = 'NEW' ) THEN ( B IS NULL )

Apply rewrite rule:

( NOT ( A = 'NEW' ) OR ( B IS NULL ) )

Minor rewrite:

( A <> 'NEW' OR B IS NULL )
onedaywhen
  • 55,269
  • 12
  • 100
  • 138
1

Edit: as mentioned in the other answers, a CHECK is the best method, not the trigger I originally suggested. Original text follows:


As dbaseman suggests, triggers are the way to go (not so). Try something like this (untested):

CREATE OR REPLACE TRIGGER test
  BEFORE UPDATE ON table1
FOR EACH ROW
WHEN (new.A = 'NEW' and new.B IS NOT NULL)
   RAISE_APPLICATION_ERROR (
     num=> -20001,
     msg=> 'B must be NULL for new rows (A = NEW)'
);
dwurf
  • 12,393
  • 6
  • 30
  • 42
  • For clarity, this solution is for Oracle. dbaseman provides a solution for what I assume is MSSQL :) – dwurf Apr 23 '12 at 01:40
  • Oracle supports tuple constraints (`CHECK`): declarative constraints should be chosen above procedural code (including triggers). – onedaywhen Apr 23 '12 at 08:01
0

Per onedaywhen, this answer is criminally wrong, and an abomination. You can use a CHECK constraint. http://msdn.microsoft.com/en-us/library/ms188258.aspx

There's not a way to make conditional constraints. You should, however be able to do the job using a trigger. That's what they're for.

http://msdn.microsoft.com/en-us/library/ms189799.aspx

CREATE TRIGGER MyTable.ConditionalNullConstraint ON MyTable.ColumnB
AFTER INSERT
AS
IF EXISTS (SELECT *
    FROM inserted
    WHERE A <> 'NEW' AND B IS NULL
    )
BEGIN
    RAISERROR ('if A is ''NEW'' then B cannot be NULL', 16, 1);
    ROLLBACK TRANSACTION;
END;
GO

Note that in the query you'll want to reference inserted which is a special object that behaves like a table, and lets you reference the row(s) that caused the trigger.

Of course, in this example you'd need to handle AFTER UPDATE also to enforce the constraint, but that's the general idea.

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • "There's not a way to make conditional constraints" Huh? This can be handled by a tuple constraint (`CHECK`) and should be preferred over a trigger in this case. – onedaywhen Apr 23 '12 at 08:00
  • 1
    @onedaywhen wow, I stand very corrected, thanks. Check constraint: http://msdn.microsoft.com/en-us/library/ms188258.aspx – McGarnagle Apr 23 '12 at 08:04