There are three tables, POLYGON, VERTEX and POLYGON_VERTICES. POLYGON_VERTICES is an intersection table allowing a many-to-many relationship between POLYGON and VERTEX.
create table polygon
(polygon_id number generated always as identity
, type varchar2(30) not null unique
, constraint polygon_pk primary key (polygon_id) )
/
create table vertex
(vertex_id number generated always as identity
, side varchar2(10) not null unique
, constraint vertex_pk primary key (vertex_id) )
/
create table polygon_vertices
(polygon_id number not null
, vertex_id number not null
, constraint polygon_vertices_uk unique (polygon_id, vertex_id)
, constraint polygon_vertices_polygon_fk foreign key (polygon_id) references polygon (polygon_id)
, constraint polygon_vertices_vertex_fk foreign key (vertex_id) references vertex(vertex_id)
)
/
We need to enforce a business rule, A Polygon Must Have At Least Three Sides. To do this we must count the number of VERTEX_ID for a given POLYGON_ID, which means the rule spans multiple records. The term for this is Assertion, which is part of the SQL standard but not (yet) implemented in Oracle. So there is no easy way of enforcing the role declaratively; we can't put an aggregation in a CHECK constraint on POLYGON_VERTICES.
There are various workarounds. We could build a FOR EACH ROW trigger to maintain a list of the new POLYGON_IDs in session memory and an AFTER STATEMENT trigger to do the count and enforce the rule. That is clunky. We could use a stored procedure to manage the inserts. That doesn't help if users can find a way to insert directly into POLYGON_VERTICES.
But there is an approach which is both safe and transparent: we build an aggregating materialized view over POLYGON_VERTICES and put the check constraint on that:
create materialized view pv_sides
refresh on commit
as
select polygon_id
, count(*) as no_of_sides
from polygon_vertices
group by polygon_id
/
alter table pv_sides
add constraint valid_polygon_ck check
( no_of_sides >= 3 )
/
create materialized view log on polygon_vertices
/
Here is a LiveSQL Demo of the full script (free OTN login required).