如何设置另一个表的唯一键检查

时间:2019-01-11 05:03:00

标签: sql oracle constraints

我创建了三个表:supplieritempurchasesupplier id与item表有关系,item id与purchase表有关系。我不想在同一itemid项目的purchase表上插入supplier。如何设置约束?

CREATE TABLE csupplier(
  supid NUMBER(10) PRIMARY KEY ,
  supname VARCHAR2(30)
 );

CREATE TABLE ctitem(
  itemid NUMBER(10) PRIMARY KEY,
  itemname VARCHAR2(50),
  supid NUMBER(10)
 );
ALTER TABLE CTITEM
  ADD CONSTRAINT CTITEM_FK1 FOREIGN KEY(SUPID )REFERENCES CSUPPLIER(SUPID );
CREATE TABLE cPurchase(
  purchaseid NUMBER(10) PRIMARY KEY,
  itemid NUMBER(10),
  purchaseqty NUMBER(10)
 );
ALTER TABLE CPURCHASE
  ADD CONSTRAINT CPURCHASE_FK1 FOREIGN KEY(ITEMID )REFERENCES CTITEM(ITEMID )

2 个答案:

答案 0 :(得分:0)

有2种解决方案来解决您的问题: 1.更改表cPurchase,然后在表中添加supid列。并在此列上创建唯一键。这样可以解决您的问题。

CREATE TABLE cPurchase(
  purchaseid NUMBER(10) PRIMARY KEY,
  itemid NUMBER(10),
  purchaseqty NUMBER(10),
  supid NUMBER(10) UNIQUE KEY
 );
  1. 如果无法在此表上进行更改,请在“插入/更新”触发器之前写入行级别。在此触发器中,编写逻辑以根据Item_id算法找到Supid,然后他们找到您的购买表中存在的该供应商的任何项目。

    CREATE [ OR REPLACE ] TRIGGER SUP_CHECK
    BEFORE INSERT
       ON cPurchase
       FOR EACH ROW 
    
    DECLARE
       L_COUNT NUMBER;
    
    BEGIN
      SELECT COUNT(*) INTO L_COUNT 
      FROM cPurchase c
      WHERE C.itemid in (Select itemid from ctitem ct where ct.supid = (Select supid 
       from ctitem  where itemid = :new.itemid)  );
    
    EXCEPTION
       WHEN ...
       -- exception handling
    
    END;
    

答案 1 :(得分:0)

  

我不想在购买时同时插入item-1和item-3

问题在于Oracle无法同时理解 的概念。它了解事务,了解DML语句,了解唯一键。因此,我们需要用Oracle可以理解的术语来描述您的问题:例如,给定的购买不能包含来自同一供应商的一项以上的商品

您的第一个问题是您的数据模型不支持这样的规则。您的cpurchase表的主键为purchaseid,这意味着您每购买一件商品就有一条记录。没有可以针对其执行规则的一组购买物品。因此,第一件事就是更改数据模型:

CREATE TABLE cPurchase(
  purchaseid NUMBER(10) PRIMARY KEY );
CREATE TABLE cPurchaseItem(
  purchaseid NUMBER(10),
  itemid NUMBER(10),
  purchaseqty NUMBER(10)
 );

ALTER TABLE CPURCHASEITEM
  ADD CONSTRAINT CPURCHASEITEM_PK PRIMARY KEY(PURCHASEID,ITEMID); 
ALTER TABLE CPURCHASEITEM
  ADD CONSTRAINT CPURCHASEITEM_FK1 FOREIGN KEY(PURCHASEID )REFERENCES CPURCHASE;
ALTER TABLE CPURCHASE
  ADD CONSTRAINT CPURCHASE_FK2 FOREIGN KEY(ITEMID )REFERENCES CTITEM(ITEMID );

现在,我们有了一个标头详细结构,可以将多个项目分配给一次购买,这意味着我们可以尝试执行该规则。

下一个问题是supplierid不是cpurchaseitem的属性。无法在对另一个表执行查询的表或列上建立检查约束。您所需要的是一个SQL断言,它是一种概念构造,可让我们定义此类规则。 Alas Oracle(以及任何其他RDBMS)目前都支持断言。

因此,我们有三个选择:

  1. 进行操作,并编写一个执行此规则的事务API。
  2. cpurchaeitem进行归一化以包含supplierid,然后在(purchaseid, supplierid)上建立唯一约束。每当您填充supplierid时,您都需要填充cpurchaseitem
  3. 编写一个after语句触发器:

(警告:此代码是通配符,可能包含错误和/或编译错误。)

create or replace trigger cpurchaseitem_trg
     after insert or update on cpurchaseitem
declare
     rec_count number;
begin
     select count(*)
     into rec_count
     from cpurchaseitem pi
         join citem I on pi.itemid = i.itemid
     group by pi.purchaseid, i.supplierid having count(*) > 1;

     if rec_count > 0 then
         raise_application_error(-20000
            , 'more than one item for a supplier!');
     end if;
 end;

坦率地说,这些解决方案都不是特别有吸引力。 API是可靠的解决方案,但可以绕开。随着购买数量随着时间的增长,触发器将遭受扩展问题(尽管可以通过编写复合触发器来缓解,这留给读者练习)。即使不是对最佳实践进行建模,反规范化也是最安全(并且可能是最有效的)解决方案。