2015-03-13 50 views
1

我正在使用Oracle数据库,并且需要创建一个如下所示的表。如何在两列上独立制定唯一约束顺序

MAP(Point_One, Poin_Two, Connection_weight). 

该表代表关于图的数据。我想创建一个表格,其中有一个约束可以防止插入已经存在的连接。

例如,表中已包含此连接:

Point_One | Point_Two | Connection_weight 
----------------------------------------- 
p_no1  | p_no2  | 10 

和约束将阻止该连接的多次插入,即使我尝试在不同的顺序添加点。 (例如:(p_no2,p_no1,10))

一个简单的UNIQUE(Point_One,Point_Two)约束是不够的。你有什么建议吗?

回答

5

您可以创建一个基于函数的索引

CREATE UNIQUE INDEX idx_unique_edge 
    ON map(greatest(point_one, point_two), 
      least(point_one, point_two)); 

我假设的point_onepoint_two数据类型是与Oracle greatestleast功能兼容。如果没有,你需要一个你自己的函数来为你的复杂数据类型选择“最大”和“最小”的点。

+0

这是非常好的想法 – Martina 2015-03-13 20:18:23

0

您可以通过使用触发器轻松实现预期的结果。

create table map (point_one number, point_two number, connection_weight number) 
/
create or replace trigger tr_map before insert on map 
for each row 
declare 
c number; 
    begin 
    select count(1) into c from map where (point_one=:new.point_one and point_two=:new.point_two) 
    or 
    (point_one=:new.point_two and point_two=:new.point_one); 
    if c>0 then 
    raise_application_error(-20000,'Connection line already exists'); 
    end if; 
end; 
/

SQL> insert into map values(1,2,10);

1 row created。 SQL> insert into map values(2,1,10); SQL> insert into map values(2,1,10); 插入到图值(2,1,10)中的第1行 * ERROR: ORA-21000:错误编号参数-100 RAISE_APPLICATION_ERROR超出 范围 ORA-06512的:在“C## MINA.TR_MAP “,第10行 ORA-04088:执行触发器'C## MINA.TR_MAP'时出错

我仍然在考虑CHECK约束,但是我没有决定是否可能与否。

+0

只要你只使用INSERT ... VALUES语句进行单行插入,它就可以工作。只要你想做一个基于集合的'INSERT ... SELECT',你就会得到一个变异的表格异常。通常,在表上定义的行级触发器(在这种情况下为'map')不能直接查询该表。所以这个行级触发器一般不允许查询'map'。您可以通过创建行级触发器和语句级触发器以及包含集合但开始变得相当复杂的包来解决该问题。 – 2015-03-13 20:24:59

+0

在Oracle 11.2中,您可以创建一个复合触发器。但通常通过触发强制约束是一个坏主意 – 2015-03-14 06:10:37