Trigger based solutions are generally either:
- Incorrect
- Slow
- Both!
This is because they have to account for concurrent actions (user 1 adds a holiday row while at the same time user 2 adds the same attendance row). Getting this correct is hard.
You could validate this with a foreign key. To do so you need to:
- Have a table of dates listing every date. Date is the PK
- A flag on this table saying if it's a holiday or not
- A unique constraint on (date, holiday)
- Attendance can then also have a holiday flag which must be N
- A foreign key from attendance to the unique constraint on dates:
SQL> create table dates (
2 calendar_date date primary key
3 check (calendar_date = trunc(calendar_date)),
4 is_holiday varchar2(1) not null
5 check (is_holiday in ('Y', 'N')),
6 unique (calendar_date, is_holiday)
7 );
Table created.
SQL>
SQL> create table attendance (
2 calendar_date date not null,
3 is_holiday varchar2(1) not null
4 check (is_holiday = 'N'),
5 foreign key (calendar_date, is_holiday)
6 references dates (calendar_date, is_holiday)
7 );
Table created.
SQL>
SQL> insert into dates values (date'2016-01-01', 'Y');
1 row created.
SQL> insert into dates values (date'2016-01-02', 'N');
1 row created.
SQL> commit;
Commit complete.
SQL> insert into attendance values (date'2016-01-01','Y');
insert into attendance values (date'2016-01-01','Y')
*
ERROR at line 1:
ORA-02290: check constraint (CHRIS.SYS_C0016315) violated
SQL> insert into attendance values (date'2016-01-01','N');
insert into attendance values (date'2016-01-01','N')
*
ERROR at line 1:
ORA-02291: integrity constraint (CHRIS.SYS_C0016316) violated - parent key not found
SQL> insert into attendance values (date'2016-01-02','N');
1 row created.
This is also fiddly though.
An assertion as Toon showed is the best way to do this. Unfortunately we don't have them yet...