14

I have a table with an "e-mail address" and "P2PMail address" column. The user is required to either enter an e-mail or P2PMail address.

If I set both to NOT NULL, then both must be filled in for a record to be created.

If I allow both to be NULL, then a user would be entirely unreachable.

If I set a UNIQUE CONSTRAINT on both columns, then the combination of them must be unique, but it says nothing about what I'm trying to do... unless I'm mis-thinking here.

What is the correct way to accomplish this? I fear that the answer is something unpleasant such as "triggers" or something. (I was never comfortable using those.)

2
  • Are you allowed to enter both? Feb 3, 2021 at 8:51
  • @Colin'tHart Good question. Hmm. I'd say yes. They may well want to also get messages in a more "instant" way but also keep a record via e-mail, so yeah, I'd say they can enter both and have duplicate notifications. Feb 3, 2021 at 8:56

2 Answers 2

27

You need a table-level check constraint:

alter table <name>
  add constraint either_email
    check (email is not null or p2pmail is not null);

If you're only allowed to enter one, but not both:

alter table <name>
  add constraint either_email
    check (email is null <> p2pmail is null);

The second form is possibly a little confusing at first glance: what it does is compare both columns' null status -- they aren't allowed to be the same.

The constraint can also be created simultaneously with the table:

create table <name> (
  ... columns ...
  constraint either_email check (email is not null or p2pmail is not null)
);
5
  • @LaurenzAlbe I strongly prefer the != syntax. But I see that internally it's converted to <> so I guess it's me that's weird. :-) Feb 3, 2021 at 9:02
  • 11
    The SQL standard defines <not equals operator> ::= <>. I think that it is a good idea to stick with standard SQL whenever there is no compelling reason to do otherwise. For one, it will make the answer applicable to other databases. Feb 3, 2021 at 9:18
  • For one, it will make the answer applicable to other databases. - true assuming that the server provider has correctly implemented the standard - or maybe they thing like @Colin'tHart? :-)
    – Vérace
    Feb 3, 2021 at 11:55
  • 4
    @Vérace: all DBMS support the standard <> (even MySQL and SQL Server). However essentially all DBMS also support the non-standard !=
    – user1822
    Feb 4, 2021 at 12:05
  • Is <> equivalent to a xor operation or are there context where the behaviour doesn't match?
    – theberzi
    Feb 5, 2021 at 8:57
13

I like using num_nonnulls for this:

alter table the_table
  add constraint check_at_least_one_email
  check (num_nonnulls(email, p2pmail) > 0);

I prefer this because it can easily be extended to multiple columns.

If you also want to deal with empty strings:

alter table the_table
  add constraint check_at_least_one_email
  check (num_nonnulls(nullif(trim(email),''), nullif(trim(p2pmail),'')) > 0);

If you require exactly one non-null value change > 0 to = 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.