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)
);
答案 0 :(得分:6)
您希望确保列 AccountID 和 RegularID 一起是唯一的,无论有多少 OpenID 值。
正如您所确定的,执行此操作的唯一方法是约束它。您在评论中注意到您已尝试使用触发器。这不约束数据库中的值,它只确保它尝试验证何时启用触发器。我希望,当你尝试它时,你得到错误“ORA-04091:正在改变,触发/功能可能看不到它。”当你从一个表中选择你在改变(插入或更新)的过程。
如果你必须限制这个,那就是你应该做的;这样做的问题是你的表没有规范化。所以,规范它。使用两个表而不是你拥有的表。
首先,您需要 RegularID 在 AccountID 上是唯一的,这意味着它应该存储在此级别。似乎tbl_UserAccounts
在此标识符上是唯一的,因此请更改此表并将 RegularID 存储在那里。
接下来,您需要一个具有与用户可能需要的 OpenID 一样多的表。这意味着您需要在 AccountID 和 OpenID 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_
为每个表的名称添加前缀。
答案 1 :(得分:5)
不,你不能这样做,请参阅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;
/