多对多关系中的约束

时间:2018-02-02 18:56:51

标签: sql oracle database-design

我试图解决问题。我有一张桌子POLYGON和一张桌子VERTEX;每个多边形可以与至少3个或更多个顶点相关联,每个顶点可以与1个或多个多边形相关联。 由于它是多对多关系,我创建了另一个包含多边形主键和顶点主键的表。 现在我如何添加多边形必须具有3个或更多顶点的约束?

注意:它不是0..3多重性而是3 .. *。我没有找到任何有同样问题的问题

1 个答案:

答案 0 :(得分:1)

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).