Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL check constraint without UDF

I have the following (fictitious) tables:

╔════════════════════╗        ╔════════════════════╗
║ Course             ║        ║ Person             ║
╠══════╦═════════════╣        ╠══════╦═════════════╣
║ ID   ║ int         ║        ║ ID   ║ int         ║
║ Name ║ varchar(50) ║        ║ Name ║ varchar(50) ║
╚══════╩═════════════╝        ╚══════╩═════════════╝

╔════════════════════╗        ╔═════════════════════╗
║ Occupation         ║        ║ B_Occupation_Person ║
╠══════╦═════════════╣        ╠══════════════╦══════╣
║ ID   ║ int         ║        ║ Person_ID    ║ int  ║
║ Name ║ varchar(50) ║        ║ Ocupation_ID ║ int  ║
╚══════╩═════════════╝        ╚══════════════╩══════╝

╔═════════════════╗
║ B_Course_Person ║
╠═══════════╦═════╣
║ Course_ID ║ int ║
║ Person_ID ║ int ║
╚═══════════╩═════╝

In the Occupation table, there are 2 rows: Student and Teacher.

The B_Occupation_Person binding table allows me to give all the persons an occupation and the B_Course_Person binding table allows me to associate a teacher with a course.

My problem is I'd like to ensure that B_Course_Person could only contain teachers.

My first idea was to add a check constraint on this table but I can only do it by using a UDF to get the person's occupation from the B_Occupation_Person table. And from what I read here, it's bad to use a UDF in a check constraint.

My second idea was to add a column Occupation in the B_Course_Person table but then I get data redundancy...

What's the best way, here?

Thanks,

like image 649
Rodolphe Avatar asked Aug 27 '12 16:08

Rodolphe


People also ask

Does check constraint allow NULL value?

CHECK constraints reject values that evaluate to FALSE. Because null values evaluate to UNKNOWN, their presence in expressions may override a constraint.

How do I view constraints on a table in SQL?

Use the view table_constraints in the information_schema schema. The column table_name gives you the name of the table in which the constraint is defined, and the column constraint_name contains the name of the constraint.

How do I find foreign key constraints in SQL?

Using SQL Server Management Studio Open the Table Designer for the table containing the foreign key you want to view, right-click in the Table Designer, and choose Relationships from the shortcut menu. In the Foreign Key Relationships dialog box, select the relationship with properties you want to view.

How do I add a check constraint in SQL Server?

Use SQL Server Management Studio In Object Explorer, expand the table to which you want to add a check constraint, right-click Constraints and select New Constraint. In the Check Constraints dialog box, select in the Expression field and then select the ellipses (...).


1 Answers

If you have a "type" column in your person table to distinguish students from teachers (which is e.g. not possible if a person can be both) you can include that type column in the primary key, and then restrict the foreign key the link table to teachers:

create table person 
(
   id integer not null, 
   person_type varchar(10) not null,
   name varchar(100),
   constraint pk_person primary key (id, person_type),
   constraint type_check check (person_type in ('student', 'teacher'))
);

create table b_occupation_person
(
  occupation_id integer not null,
  person_id integer not null,
  person_type varchar(10) not null,
  constraint fk_occupation_person 
      foreign key (person_id, person_type) 
      references person (id, person_type),
  constraint type_check check (person_type = 'teacher')
);

The person_type is redundant in the b_occupation_person but as far as I can tell it's the only option to create this type of constraint in a declarative manner.

Due to the foreign key and the check constraint it's not possible to insert anything else but a teacher into b_occupation_person.

But again: this only works if you actually can distinguish teachers from students (and if a teacher cannot be a student).

If you need a person to be teacher and student (and you don't have a "person_type"), you might think about a teacher table that simply references the person table:

create table person 
(
   id integer not null primary key, 
   name varchar(100)
);

create table teacher 
(
  person_id integer not null primary key,
  foreign key (person_id) references person (id)
);

create table b_occupation_person
(
  occupation_id integer not null,
  teacher_id integer not null,
  foreign key (teacher_id) references teacher (person_id)
);

The drawback of this is that a person that is teacher needs to be inserted twice (once into person, once into teacher).

In PostgreSQL you could take advantage of table inheritance and define teacher to inherit from person. Thus any insert into teacher would automatically create a person as well (so you don't need to insert such a person twice).

like image 140
a_horse_with_no_name Avatar answered Sep 23 '22 00:09

a_horse_with_no_name