SQL: composite primary key, constraint on cascade - sql

I have 3 tables: A, B, C.
The table A contains the attributes: a (primary key)
The table B contains the attributes: a, c (both of them compose a composite primary key)
The table C contains the attributes: c (primary key)
How should I set a constraint to remove the elements of B on cascade, when I remove an entry in A? (using Oracle DBMS).
I have tried this:
ALTER TABLE A ADD CONSTRAINT constraint FOREIGN KEY (a) REFERENCES B (a) ON DELETE CASCADE
But next error is thrown:
ORA-02270: no matching unique or primary key for this column-list
Thanks
Edited:
I´ve added two foreign keys to the table B:
ALTER TABLE B ADD CONSTRAINT FOREIGN KEY (a) REFERENCES A (a) ON DELETE CASCADE
ALTER TABLE B ADD CONSTRAINT FOREIGN KEY (c) REFERENCES C (c) ON DELETE CASCADE
Then, I will remove elements in table B, and the entries in A and C are also removed.

To meet this requirement:
Then, I will remove elements in table B, and the entries in A and C are also removed.
You need to make the B table a parent table for tables A and C by a adding foreign key constraint, that references B table to A and C tables.
Note that the number of referencing columns have to match the number of referenced columns:
create table A(
tab_id number primary key
);
create table B(
col1 number,
col2 number,
constraint PK_Key primary key(col1, col2)
);
create table C(
tab_id number primary key
);
alter table A add ( col1 number
, col2 number
, constraint fk_AB foreign key(col1, col2)
references B(col1, col2) on delete cascade);
alter table C add ( col1 number
, col2 number
, constraint fk_CB foreign key(col1, col2)
references B(col1, col2) on delete cascade);

This is a bit of a guess, but I am assuming table B is a child of of both A and C (perhaps B is a bridging (or cross-reference) table between A and C, where A and C share a many to many relationship).
Table A \*---1 Table B 1---\* Table C (* = many, 1 = one)
I also notice that the FK you are introducing in an identifying FK (by virtue of B(a) being a part of B's primary key).
That makes Table A the parent in this relationship, and B the child. In my experience, any FKs need to be added to the child side of the relationship (in this case, table B).
I'm no Oracle expert, but does this not make more sense?...
ALTER TABLE B ADD CONSTRAINT constraint FOREIGN KEY (a) REFERENCES A (a) ON DELETE CASCADE
This should remove all B rows referring to the PK of any A rows you choose to remove. BUT, I'm not an Oracle expert, so take only at face value until someone with Oracle smarts can confirm (or bomb) my explanation.

Related

Ecto - Foreign key constraint on table A for table B or C, but not both B & C

Let's say that table A can belong to either table B or table C, but not both.
It seems that it is possible to use foreign key constraints for this scenario in SQL Server.
How could this relationship be represented in Elixir's Ecto library?
You can structure this as using a separate column for each foreign key constraint and then guaranteeing that only one is populated:
create table A (
. . .
b_id int,
c_id int,
foreign key (b_id) references b(b_id),
foreign key (c_id) references c(c_id),
check (b_id is null or c_id is null)
)

oracle sql - on delete cascade in both directions

I have table A and table B, where the primary key of table B references the primary key of table A. If i use on delete cascade on table B how can i ensure that rows of table A are deleted when i delete a row in table B?
Edit:
Merging is unfortunately not the solution to my problem.
In my database i have three tables that are relevant for this situation.
This is what it looks like:
CREATE TABLE TABLE_A
(
TABLE_A_ID INTEGER NOT NULL,
...
PRIMARY KEY(TABLE_A_ID)
);
CREATE TABLE TABLE_B
(
TABLE_B_ID INTEGER REFERENCES TABLE_A(TABLE_A_ID) ON DELETE CASCADE,
...
PRIMARY KEY(TABLE_B_ID)
);
CREATE TABLE TABLE_C
(
TABLE_C_ID INTEGER REFERENCES TABLE_A(TABLE_A_ID) ON DELETE CASCADE,
...
PRIMARY KEY(TABLE_C_ID)
);
Although it is possible to create rows in A on its own i dont want any rows that are only in A.
Now if I delete a row in B, I want the row in A with the same ID deleted too. I dont want any ID that is only in A, or only not in A.

Foreign Key tuple constraint multiple tables.

I have 3 tables, and would like to create a new table which has 3 columns which are foreign keys to the other 3 tables ids.
CREATE TABLE tableABC(
tblA INTEGER REFERENCES tableA,
tblB INTEGER REFERENCES tableB,
tblC INTEGER REFERENCES tableC,
newColumn Integer,
PRIMARY KEY (tblA, tblB, tblC) ----- Is this efficient ?
);
CREATE TABLE tableA(
tableA_id SERIAL PRIMARY KEY
);
CREATE TABLE tableB(
tableB_id SERIAL PRIMARY KEY
);
CREATE TABLE tableC(
tableC_id SERIAL PRIMARY KEY
);
Now for table tableABC I would like the column pair (tblA, tblB, tblC) to be unique. When querying this table, search will be based on those three columns. Which way would be most efficient.
I did some search and could not find a way to have foreign key constraint on 3 columns from 3 different tables.
For example FOREIGN KEY (b, c) REFERENCES other_table (c1, c2) references one table. Is it possible to reference 3 different tables.
Tnx.

Referencing a two column primary key with multiple foreign keys

Take the following two tables in Oracle:
Create Table A
( A int, B int, C int,
Constraint pk_ab Primary Key(A, B),
Unique (C)
);
Create Table B
( D int, E int, F int,
Constraint fk_d Foreign Key (D) References A(A),
Constraint fk_e Foreign Key (E) References A(B)
);
Why doesn't this statement work? Or more specifically, why shouldn't it work? The reason I'm trying to create this type of relation is say, in the future, I want to delete B.D, but keep the relation FK_E.
I'm getting the error:
ORA-02270: no matching unique or primary key for this column-list
"Why doesn't this statement work? Or more specifically, why shouldn't
it work? "
You have defined the primary key on A as a compound of two columns (A,B). Any foreign key which references PK_AB must match those columns in number. This is because a foreign key must identify a single row in the referenced table which owns any given row in the child table. The compound primary key means column A.A can contain duplicate values and so can column A.B; only the permutations of (A,B) are unique. Consequently the referencing foreign key needs two columns.
Create Table B
( D int, E int, F int,
Constraint fk_de Foreign Key (D,E) References A(A,B)
);
"Since there are multiple PK's that table B references"
Wrong. B references a single primary key, which happens to comprise more than one column,
" say, in the future, I want to delete B.D, but keep the relation
fk_e. "
That doesn't make sense. Think of it this way: D is not a property of B, it is an attribute B inherits through its dependence on table A.
One way to avoid this situation is to use a surrogate (or synthetic) key. Compound keys are often business keys, hence their columns are meaningful in a business context. One feature of meaningful column values is that they can change, and cascading such changes to foreign keys can be messy.
Implementing a surrogate key would look like this:
Create Table A
( id int not null, A int, B int, C int,
Constraint pk_a Primary Key(ID),
constraint uk_ab Unique (A,B)
);
Create Table B
( a_id int, F int,
Constraint fk_n_a Foreign Key (A_ID) References A(ID)
);
Of course, you could kind of do this using the schema you posted, as you already have a single column constraint on A(C). However, I think it is bad practice to reference unique constraints rather than primary keys, even though it's allowed. I think this partly because unique constraints often enforce a business key, hence meaning, hence the potential for change, but mainly because referencing primary keys just is the industry standard.
Try create two separate indexes for column's A and B before creating table B
CREATE INDEX a_idx ON A (A);
CREATE INDEX b_idx ON A (B);
But probably you need a compound FK on table B
Create Table B
( D int, E int, F int,
Constraint fk_d Foreign Key (D,E) References A(A,B)
);
A foreign key always references a PK of another table. Neither A nor B
alone are PK's.. You have multiple PK's.
Constraint fk_d Foreign Key (D,E) References A(A,B)
Also the database cannot validate a partial null multiple foreign key, so I think the check constraint also needs to be added to the table.
alter table B add constraint check_nullness
check ( ( D is not null and E is not null ) or
( D is null and E is null ) )

How to declare a foreign key with an OR condition using Oracle?

I have a table (A) whose primary key is either a foreign key to table (B) or table (C).
create table A (
akey number,
txt varchar2(10)
);
create table B (
bkey number,
txt varchar2(10)
);
create table C (
ckey number,
txt varchar2(10)
);
What I want is something like:
alter table A add constraint BorCkey foreign key (akey) references B(bkey)` or C(ckey);
Is this possible?
No, that sort of thing is not possible in Oracle.
Your options generally are
Create two different columns (bkey and ckey) in A where bkey references B.bkey and ckey references C.ckey and create a constraint that ensures that only one is non-NULL at any point in time.
Create some sort of "combined B & C" entity that B & C have foreign keys to and make the foreign key in A reference the key of this combination entity.
If you want a constraint that ensures that exactly one of two columns is NULL and one is NOT NULL for any row
create table one_key(
col1 number,
col2 number,
check( nvl2(col1,1,0) + nvl2(col2,1,0) = 1 )
)
A foreign key constraint is to one foreign table.
That means you'd need to use two ALTER TABLE statements in this situation to setup foreign keys to reference the two tables. There's no opportunity in there to specify an OR in the relationship -- the value in A.akey would have to exist in both B.bkey and C.ckey at the same time. For example, if B.bkey has a value of NULL, but C.ckey does not -- then A.akey can never have a value of NULL. Foreign keys are deferrable in Oracle, but the behavior described is what you will encounter if both foreign keys are enabled at the same time -- you won't be able to enable a constraint if all the values don't satisfy the relationship.
You need to review your needs for how to simplify the relationship so it doesn't need two tables to make this work.
Sounds like you have some form of subtype/supertype relationship going on.
A typical example is 'PERSON' which may be either a 'CUSTOMER' or a 'SUPPLIER'.
You might have, in the PERSON table the unique key of PERSON_ID plus an attribute of PERSON_TYPE ('CUST' or 'SUPP'). If you create the primary key on PERSON_ID,PERSON_TYPE you can reference that in the subtype tables (SUPPLIER/CUSTOMER).
Then you add a unique constraint on the person_id to ensure that any value of person_id must be either a customer or supplier but not both, and check constraints on the subtype tables so that only one type is represented in the table.
create table person
(person_id number,
person_type varchar2(4),
name varchar2(10),
constraint person_pk primary key (person_id, person_type),
constraint person_id_uk unique (person_id));
create table supplier
(supplier_id number,
supplier_type varchar2(4),
blah varchar2(10),
constraint supplier_pk primary key (supplier_id, supplier_type),
constraint supp_pers_fk foreign key (supplier_id, supplier_type)
REFERENCES person (person_id, person_type)
)
/
alter table supplier add constraint supp_type_ck check (supplier_type = 'SUPP');
Its not pretty but types/subtypes are more of an object concept than a relational one.
My solution inspired by Justin:
CREATE OR REPLACE TRIGGER abc
BEFORE INSERT OR UPDATE ON a
FOR EACH ROW
DECLARE
v_testB NUMBER:= 0;
v_testC NUMBER:= 0;
BEGIN
SELECT
COUNT(bkey)
INTO
v_testB
FROM
b
WHERE
bkey = :new.aKey;
SELECT
COUNT(ckey)
INTO
v_testC
FROM
c
WHERE
ckey = :new.aKey;
IF ((v_testB + v_testC) <> 1) THEN
RAISE_APPLICATION_ERROR(-20002,'Foreign key to B or C missing.');
END IF;
END;
/
SHOW ERRORS TRIGGER abc
Create a materialized view that unions tables B & C, and point your FK constraint to the view