Referencing a two column primary key with multiple foreign keys - sql

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 ) )

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)
)

Attribute referencing two foreign keys

I created a table like this in SQL
create table WEATHER_STATION.Humidade
(
valor_observado int ,
data_observacao date ,
unidade_medida varchar(15),
id_observacao int identity(1,1) primary key,
estacao varchar(30)
foreign key (estacao) references WEATHER_STATION.Pessoa_singular(estacao_nome),
foreign key (estacao) references WEATHER_STATION.Estaçao_meteorologica(localidade)
)
Then, I insert values with this code:
INSERT INTO WEATHER_STATION.Humidade
VALUES (83,'2020-01-24','%','Albergaria(amadora)')
INSERT INTO WEATHER_STATION.Humidade
VALUES (83,'2020-01-23','%','Aveiro')
With both I got this error :
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Humidade__estaca__00CA12DE". The conflict occurred in database "p4g8", table "WEATHER_STATION.Estaçao_meteorologica", column 'localidade'.
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Humidade__estaca__7FD5EEA5". The conflict occurred in database "p4g8", table "WEATHER_STATION.Pessoa_singular", column 'estacao_nome'.
My question is: is it possible that one attribute references two foreign keys from different tables?
It certainly can.
But the way you phrase it is misleading. A column does not "point" to another table. A foreign key "relates" column(s) from one table to a key on another table. When a column is related to more than one key on a foreign table, I call them "forking foreign keys", but that's just me.
Consider the following example:
create table a (
b int primary key not null,
c int unique not null
);
create table d (
e int,
f int,
constraint fk1 foreign key (e) references a (b),
constraint fk2 foreign key (e) references a (c),
constraint fk3 foreign key (f) references a (b),
constraint fk4 foreign key (f) references a (c)
);
In this case you have four foreign keys with two columns. Using your "point" lingo you can say that:
e -> b
e -> c
f -> b
f -> c
So e points twice and f points twice as well. Also b and c are pointed twice each one.
Finally, one interesting side effect is that, since e points twice to the table a that single value may be actually pointing to two different rows at once. This could be very tricky for INSERTs, UPDATEs, and DELETEs. The errors you are seeing are probably related to this side effect. You'll need to be careful when modifying the data to keep FKs valid.

Foreign key SQL syntax

Let's suppose that there are two tables table1 and table2 where the second one references the first one.
I was wondering whether the following two forms of defining their foreign key relationship results in the same table structure of the second table.
CREATE TABLE table1(a INT, b INT, PRIMARY KEY(a, b));
1)
CREATE TABLE table2(a INT, b INT,
FOREIGN KEY(a, b) REFERENCES table1(a, b)
);
2)
CREATE TABLE table2(a INT, b INT,
FOREIGN KEY(a) REFERENCES table1(a),
FOREIGN KEY(b) REFERENCES table1(b)
);
I would presume that there is no difference but couldn't find any reference to support this claim.
First, this syntax is not correct:
CREATE TABLE table1 (
a INT PRIMARY KEY,
b INT PRIMARY KEY
);
A primary key is primary because only one can be defined. Presumably, you intend a composite primary key:
CREATE TABLE table1 (
a INT,
b INT,
PRIMARY KEY (a, b)
);
The foreign key relationships are entirely different. The second creates two foreign key relationships. With a composite primary key, it would fail, because the reference should be to a primary key (or at least a unique key).

SQL: composite primary key, constraint on cascade

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.

Check if data exists in another table on insert?

Table A
(
Table_A_ID int
)
Table B
(
Table_B_ID int
Value int
)
Say I want to insert data into Table B, where 'Value' would be the same as a Table_A_ID.
How would I make a constraint or check that the data actually exists in the table on insertion?
You probably need to enforce data integrity not only on INSERT into Table B, but also on UPDATE and DELETE in both tables.
Anyway options are:
FOREIGN KEY CONSTRAINT on Table B
TRIGGERs on both tables
As a last resort if for some reason 1 and 2 is not an option STORED PROCEDUREs for all insert, delete update operations for both tables
The preferred way to go in most cases is FOREIGN KEY CONSTRAINT.
Yap, I agree with #peterm.
Cause, if your both Table_A_ID and Table_B_Id are primary keys for both tables, then you don't even need two tables to store the value. Since, your two tables are seems to be on 'one-to-one' relationship. It's one of the database integrity issues.
I think you didn't do proper normalisation for this database.
Just suggesting a good idea!
I found this example which demonstrates how to setup a foreign key constraint.
Create employee table
CREATE TABLE employee (
id smallint(5) unsigned NOT NULL,
firstname varchar(30),
lastname varchar(30),
birthdate date,
PRIMARY KEY (id),
KEY idx_lastname (lastname)
) ENGINE=InnoDB;
Create borrowed table
CREATE TABLE borrowed (
ref int(10) unsigned NOT NULL auto_increment,
employeeid smallint(5) unsigned NOT NULL,
book varchar(50),
PRIMARY KEY (ref)
) ENGINE=InnoDB;
Add a constraint to borrowed table
ALTER TABLE borrowed
ADD CONSTRAINT FK_borrowed
FOREIGN KEY (employeeid) REFERENCES employee(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
NOTE: This tells MySQL that we want to alter the borrowed table by adding a constraint called ‘FK_borrowed’. The employeeid column will reference the id column in the employee table – in other words, an employee must exist before they can borrow a book.
The final two lines are perhaps the most interesting. They state that if an employee ID is updated or an employee is deleted, the changes should be applied to the borrowed table.
NOTE: See the above URL for more details, this is just an excerpt from that article!
Create a foreign key constraint on the column 'Value' on table B that references the 'Table_A_ID' column.
Doing this will only allow values that exist in table A to be added into the 'Value' field of table B.
To accomplish this you first need to make Table_A_ID column the primary key for table A, or it at least has to have some sort of unique constraint applied to it to be a foreign key candidate.
BEGIN TRANSACTION -- REMOVE TRANSACTION AND ROLLBACK AFTER DONE TESTING
--PUT A PRIMARY KEY ON TABLE A
CREATE TABLE A
( Table_A_ID int CONSTRAINT PK_A_Table_A_ID PRIMARY KEY)
--ON VALUE ADD A FOREIGN KEY CONSTRAINT THAT REFERENCEs TABLE A
CREATE TABLE B
( Table_B_ID int,
[Value] int CONSTRAINT FK_B_Value_A REFERENCES A(Table_A_ID)
)
-- TEST VALID INSERT
INSERT A (Table_A_ID) VALUES (1)
INSERT B (Table_B_ID, [Value]) VALUES (1,1)
--NOT ALLOW TO INSERT A VALUE THAT DOES NOT EXIST IN A
--THIS WILL THROW A FOREIGN KEY CONSTRAINT ERROR
INSERT B (Table_B_ID, [Value]) VALUES (1,2) -- 2 DNE in table A
ROLLBACK
Note: there is no magic to 'FK_B_Value_A' or 'PK_A_Table_A_ID' it simply a naming convention and be called anything. The syntax on the foreign key and primary key lines work like this:
column-definition CONSTRAINT give-the-constraint-a-name REFERENCES table-name ( table-column )
column-definition CONSTRAINT give-the-constraint-a-name PRIMARY KEY