【问题标题】:Check Constraint Calling a Function Oracle SQL developer检查约束调用函数 Oracle SQL 开发人员
【发布时间】:2017-01-04 23:37:35
【问题描述】:

Oracle SQL 开发人员可以做这样的事情吗

CREATE FUNCTION fnCheckValid(accountidd IN NUMBER) 
RETURN NUMBER 
   IS retval NUMBER(4,0);
   BEGIN 
      SELECT COUNT(accountid_fk) 
      INTO retval 
      FROM tbl_AccountAuthentications 
      WHERE accountid_fk = accountidd; 
      RETURN(retval); 
    END;
/


ALTER TABLE tbl_AccountAuthentications
ADD CONSTRAINT chkCheckvalid CHECK(fnCheckValid(accountid_fk) <= 1);

我不断收到的错误是

Error starting at line 999 in command:
ALTER TABLE tbl_AccountAuthentications
ADD CONSTRAINT chkCheckvalid CHECK(fnCheckValid(accountid_fk) <= 1)
Error report:
SQL Error: ORA-00904: "FNCHECKVALID": invalid identifier
00904. 00000 -  "%s: invalid identifier"
*Cause:    
*Action:

正在创建该函数,我可以找到它,但是当我尝试调用它时,我不断收到该错误

这就是我想要实现的目标

AccountID    RegularID     OpenID
 1            5             null
 1            null          10
 1            null          11
 1            6                             <-- Forbidden

这样一个用户就不能创建 2 个常规帐户,但可以拥有任意数量的 OpenID 帐户

表格设置如下

CREATE TABLE tbl_AccountAuthentications(
        newAuthID NUMBER(4,0)
          CONSTRAINT naid_pk PRIMARY KEY,        
        accountid_fk NUMBER(4,0)
          CONSTRAINT accid_fk 
            REFERENCES tbl_UserAccounts(account_id),
        regularid_fk NUMBER(4,0)
          CONSTRAINT rgid_fk
            REFERENCES tbl_StrongRoom(password_id),
        openid_fk NUMBER(4,0)
          CONSTRAINT opid_fk
            REFERENCES tbl_OpenID(openid)
);

【问题讨论】:

  • 您只是想确保 accountid_fk 是唯一的吗?
  • 用我想做的编辑,同样,'SYS'在那里作为测试忘记删除它,但删除'SYS'后仍然有这个错误
  • 普通账户和开户是同一张表吗?
  • 用请求的数据编辑,是的,它们在同一个表中
  • Oracle 中的CHECK 约束不能调用用户定义的函数。参考here.

标签: sql oracle oracle-sqldeveloper


【解决方案1】:

不,您不能这样做,请参阅Restrictions on Check Constraints

  • 调用用户定义的函数

但是您可以使用虚拟列来解决问题

ALTER TABLE tbl_AccountAuthentications ADD (fnCheck NUMBER GENERATED ALWAYS AS (fnCheckValid(accountid_fk)) VIRTUAL);


ALTER TABLE tbl_AccountAuthentications
ADD CONSTRAINT chkCheckvalid CHECK(fnCheck <= 1);

注意,函数必须是 DETERMINISTIC,否则它不起作用。 Oracle 不会验证您的函数是否实际上是确定性的,它只是检查关键字。这个是允许的(尽管它根本没有任何意义):

CREATE OR REPLACE FUNCTION DET_FUNCTION RETURN NUMBER DETERMINISTIC IS 
BEGIN 
    RETURN DBMS_RANDOM.RANDOM();
END;
/

【讨论】:

  • 值得一提的是,只有在函数是确定性的情况下才允许这样做?而在 12c 中,虚拟列可以是不可见的。
【解决方案2】:

您要确保列 AccountIDRegularID 一起是唯一的,无论 OpenID 有多少个值。

正如您所确定的,这样做的唯一方法是约束它。您在 cmets 中注意到您已尝试使用触发器。这限制数据库中的值,它只确保它尝试验证触发器何时启用。我希望,当您尝试它时,您会收到错误 “ORA-04091: is mutating, trigger/function may not see it.” 当您从您所在的表中进行选择时更改过程(插入或更新)。

如果你必须限制这一点,那么这就是你应该做的;这样做的问题是您的表格没有标准化。所以,正常化它。使用两张表,而不是您现有的一张。

首先,您需要您的 RegularIDAccountID 上是唯一的,这意味着它应该存储在这个级别。看起来tbl_UserAccounts 在此标识符上是唯一的,因此请更改此表并将您的 RegularID 存储在那里。

接下来,您需要一个表,该表的 OpenID 数量与用户可能需要的一样多。这意味着您需要一个在 AccountIDOpenID 1 上唯一的表。

create table openid_account_auth (
   , account_id number(4,0)
   , open_id number(4,0)
   , constraint pk_openid_account_auth
       primary key (account_id, open_id)
   , constraint fk_openid_account_auth_accid
       foreign key (account_id)
       references tbl_UserAccounts(account_id)
   , constraint fk_openid_account_auth_open
       foreign key (open_id)
       references tbl_OpenID (openid)
     );

这个表上有一点(和你自己的),这意味着多个帐户可以有相同的OpenID。如果您不打算这样做,那么您应该在 tbl_OpenID 中添加 AccountID 作为外键,这是确保每个 OpenID 与一个相关联的唯一方法,只有一个,AccountID

然后,您可以创建一个视图,以便以相同的方式获取信息,如果您真的觉得需要使用它。我不确定你为什么会这样做。

create or replace view AccountAuthentications as
 select account_id, regular_id, null
   from user_accounts
  union all
 select account_id, null, open_id
   from openid_account_auth;

简单地说,除非受到严格的限制,否则您应该始终以自然的方式存储数据并使用数据库来确保保持完整性。如果您随后需要稍微不同地使用数据,您可以使用视图或materialized views 等来执行此操作。

1。很抱歉,我不能让自己在每个表的名称前加上 tbl_

【讨论】:

    猜你喜欢
    • 2021-03-27
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 2020-05-09
    • 1970-01-01
    • 2022-07-19
    • 1970-01-01
    相关资源
    最近更新 更多