我想让同一张表的2列中的值唯一。我想建立一个规则,使任何值都不能再次出现在表的2列中。
例如,考虑表mail_address_book (pk_serial_no, address_a, address_b)
和address_a
和address_b
是我要建立相互唯一性的2列。
如果有人尝试运行以下插入语句,则应为:
create table mail_address_book (pk_serial_no number, address_a varchar2(5), address_b varchar2(5))
insert into mail_address_book(1,'A','B'); --Allow
insert into mail_address_book(2,'B','A'); --Error
insert into mail_address_book(3,'C','A'); --Error
insert into mail_address_book(4,'C','C'); --Error
insert into mail_address_book(5,'C',null); --Allow
答案 0 :(得分:2)
如果要使同一表中2列的值唯一,那么数据模型似乎存在问题-两个或更多列包含相同类型的信息。也许最好的解决方案是重新分配DM并创建单独的表:
function addUser(event,rc,prc)
{
LOCAL.userBean = populateModel("userBean").init(5,prc.siteid,event.getValue('userid',0));
rc.user = securityService.getUser(LOCAL.userBean);
LOCAL.userBean.setMethod(3);
rc.genderList=globalsService.getGlobals(LOCAL.userBean);
LOCAL.userBean.setMethod(7);
rc.stateList=globalsService.getGlobals(LOCAL.userBean);
event.setLayout("Window");
event.setView("purchase/addUser");
}
作为解决方法,您可以将物理表转换为视图,然后将该视图而不是表用于所有查询和DML语句。考虑以下示例:
create table mail_address_book (serial_no number primary key /* maybe FK to somewhat */)
/
create table mail_address_entries (
serial_no number, addrno number, address varchar2(5) unique,
constraint pk_fk_mail_address_entries primary key(serial_no, addrno),
constraint fk_mail_address_entries foreign key (serial_no) references mail_address_book (serial_no))
/
插入测试数据:
create table mail_address_entries (
pk_serial_no number, addrno number, address varchar2(5) unique,
constraint pk_mail_address_entries primary key (pk_serial_no, addrno)
)
/
create or replace view mail_address_book as
select a.pk_serial_no, a.address address_a, b.address address_b
from mail_address_entries a
join mail_address_entries b on (
b.pk_serial_no = a.pk_serial_no and a.addrno = 1 and b.addrno = 2
);
create or replace trigger trig_mail_address_book
instead of insert on mail_address_book
begin
if inserting then -- the same for updating, deleting
insert into mail_address_entries values (:new.pk_serial_no, 1, :new.address_a);
insert into mail_address_entries values (:new.pk_serial_no, 2, :new.address_b);
end if;
end;
/
结果:
create or replace type addrRow force is object (pk_serial_no number, address_a varchar2(5), address_b varchar2(5));
/
create or replace type addrRows is table of addrRow;
/
exec dbms_errlog.create_error_log (dml_table_name => 'mail_address_book');
declare
testdata addrRows;
begin
testdata := addrRows (
addrRow (1, 'A', 'B'),
addrRow (2, 'B', 'A'),
addrRow (3, 'C', 'A'),
addrRow (4, 'C', 'C'),
addrRow (5, 'C', null),
addrRow (6, null, null),
addrRow (7, 'D', 'E'),
addrRow (8, 'E', 'F')
);
for r in (select * from table (testdata)) loop
begin
insert into mail_address_book values (r.pk_serial_no, r.address_a, r.address_b);
exception when dup_val_on_index then
insert into err$_mail_address_book (pk_serial_no, address_a, address_b, ora_err_mesg$)
values (r.pk_serial_no, r.address_a, r.address_b, 'error');
end;
end loop;
end;
/
答案 1 :(得分:2)
与OP提出的方法类似的方法,使用一个具有唯一性的伪表和一个触发器来管理合法动作:
create table hack_table (address varchar2(5) primary key);
create trigger hack_trigger
before insert or update or delete on mail_address_book
for each row
begin
if inserting then
if :new.address_a is not null then
insert into hack_table (address) values (:new.address_a);
end if;
if :new.address_b is not null then
insert into hack_table (address) values (:new.address_b);
end if;
elsif updating then
-- maybe skip this is column values have swapped
if :old.address_a is null and :new.address_a is not null then
insert into hack_table (address) values (:new.address_a);
elsif :old.address_a is not null and :new.address_a is null then
delete from hack_table where address = :old.address_a;
elsif :new.address_a != :old.address_b then
update hack_table set address = :new.address_a where address = :old.address_a;
end if;
if :old.address_b is null and :new.address_b is not null then
insert into hack_table (address) values (:new.address_b);
elsif :old.address_b is not null and :new.address_b is null then
delete from hack_table where address = :old.address_b;
elsif :new.address_b != :old.address_a then
update hack_table set address = :new.address_b where address = :old.address_b;
end if;
else
delete from hack_table where address in (:old.address_a, :old.address_b);
end if;
end;
/
显然选择了更合适的对象名称和列大小* 8-)
然后一些示例插入得到:
insert into mail_address_book (pk_serial_no, address_a, address_b) values (1,'A','B'); --Allow
1 row inserted.
insert into mail_address_book (pk_serial_no, address_a, address_b) values (2,'B','A'); --Error
ORA-00001: unique constraint (STACKOVERFLOW.SYS_C00141064) violated
insert into mail_address_book (pk_serial_no, address_a, address_b) values (3,'C','A'); --Error
ORA-00001: unique constraint (STACKOVERFLOW.SYS_C00141064) violated
insert into mail_address_book (pk_serial_no, address_a, address_b) values (4,'C','C'); --Error
ORA-00001: unique constraint (STACKOVERFLOW.SYS_C00141064) violated
insert into mail_address_book (pk_serial_no, address_a, address_b) values (5,'C',null); --Allow
1 row inserted.
insert into mail_address_book (pk_serial_no, address_a, address_b) values (6,'D','C'); --Error
ORA-00001: unique constraint (STACKOVERFLOW.SYS_C00141064) violated
insert into mail_address_book (pk_serial_no, address_a, address_b) values (7,'C','D'); --Error
ORA-00001: unique constraint (STACKOVERFLOW.SYS_C00141064) violated
insert into mail_address_book (pk_serial_no, address_a, address_b) values (8,'D','E'); --Allow
1 row inserted.
insert into mail_address_book (pk_serial_no, address_a, address_b) values (9,'E','F'); --Error
ORA-00001: unique constraint (STACKOVERFLOW.SYS_C00141064) violated
最终只能得到:
PK_SERIAL_NO ADDRE ADDRE
------------ ----- -----
1 A B
5 C
8 D E
答案 2 :(得分:1)
我想在不使用功能索引和触发器的情况下以某种方式创建查找表的解决方案,但是由于无法找到解决方案,并且由于缺乏完整的答案,请在下面找到我的方法:
请在下面找到代码流/算法。.我正在使用具有唯一索引的数据库查找表,但是该表对原始表的最终用户隐藏了。
create table distinct_add (address_code varchar2(25) not null);
create unique index distinct_add_uq1 on distinct_add(address_code);
Trigger before insert or update or delete on mail_address_book
------
IF INSERTING OR UPDATING
Then
IF UPDATING
IF(:old.address_a is not null and nvl(:old.address_a,'garbage') != nvl(:new.address_a,'garbage'))
THEN
delete :old.address_a from distinct_add
catch exception raise error
END IF
IF(:old.address_b is not null and nvl(:old.address_b,'garbage') != nvl(:new.address_b,'garbage'))
THEN
delete :old.address_b from distinct_add
catch exception raise error
END IF
END IF
IF(:new.address_a is not null and nvl(:old.address_a,'garbage') != nvl(:new.address_a,'garbage'))
THEN
insert :new.address_a into distinct_add
catch exception raise error
END IF
IF(:new.address_b is not null and nvl(:old.address_b,'garbage') != nvl(:new.address_b,'garbage'))
THEN
insert :new.address_b into distinct_add
catch exception raise error
END IF
END IF
IF DELETING
Then
delete nvl(:new.address_a,'garbage') nvl(:new.address_b,'garbage') from distinct_add
catch exception raise error
END IF