Is it possible in Oracle SQL developer to do something like this
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);
The error i keep getting is
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:
The function is being created and i can find it but when i am trying to call it i keep getting that error
this is what i am trying to achieve
AccountID RegularID OpenID
1 5 null
1 null 10
1 null 11
1 6 <-- Forbidden
so that a user cannot make 2 regular accounts but can have as many OpenID accounts as he wants
The table is set up as follows
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)
);
No you can't do that, see Restrictions on Check Constraints:
- Calls to user-defined functions
But you can make a workaround using virtual columns
ALTER TABLE tbl_AccountAuthentications ADD (fnCheck NUMBER GENERATED ALWAYS AS (fnCheckValid(accountid_fk)) VIRTUAL);
ALTER TABLE tbl_AccountAuthentications
ADD CONSTRAINT chkCheckvalid CHECK(fnCheck <= 1);
Note, function has to be DETERMINISTIC, otherwise it does not work. Oracle does not verify whether your function is actually deterministic, it just checks for the keyword. This one is allowed (although it does not make any sense at all):
CREATE OR REPLACE FUNCTION DET_FUNCTION RETURN NUMBER DETERMINISTIC IS
BEGIN
RETURN DBMS_RANDOM.RANDOM();
END;
/
You want to ensure that the columns AccountID and RegularID, together, are unique, no matter how many values of OpenID there are.
The only way of doing this, as you've determined, is to constrain it. You note in the comments that you've experimented with triggers. This does not constrain the values within the database, it only ensures that it attempts to verify when the trigger is enabled. I expect that, when you attempted it, you got the error "ORA-04091: is mutating, trigger/function may not see it." as you're selecting from a table you're in the processes of changing (insert or update).
If you have to constrain this then that's what you should do; the problem with doing so is that your table is not normalised. So, normalise it. Use two tables instead of the one you have.
Firstly, you need your RegularID to be unique over AccountID this means it should be stored at this level. It appears as though tbl_UserAccounts
is unique on this identifier so alter this table and store your RegularID there.
Next, you want a table that has as many OpenID s as a user might want. This means that you need a table unique on AccountID and 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)
);
One point on this table (and your own), it means that multiple accounts can have the same OpenID. If you did not intend this then you should add AccountID as a foreign key in tbl_OpenID
, which would be the only way to ensure that each OpenID is associated with one, and only one, AccountID.
You can then create a view in order to get the information in the same manner, if you really feel the need to use this. I'm not certain why you would.
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;
Simply put, unless under severe constraints, you should always store data at it's natural level and use the database to ensure that integrity is maintained. If you then need to use the data slightly differently you can use views, or materialized views etc., to do so.
1. I'm sorry but I can't bring myself to prefix the name of every table with tbl_
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With