Create Foreign key from a primary key - mysql

I'm using MySQL & I need to create following tables.
1st table : having 3 attributes A,B,C
2nd table : having 2 attributes B,D
3rd table : having 2 attributes C,E
Now, A is the primary key.
I need to create the 2nd-3rd tables, such that the values in B for 2nd table should be already present in 1st table's B attribute, & similarly values in C of 3rd table should be already present in C of 1st table.
My attempts :
1) put A in both, 2nd & 3rd tables, & keep them as foreign key referencing A of 1st table, & put on update cascade in each.
2) keep check constraints for both 2nd & 3rd tables, although I couldnt find proper syntax for the check constraints when attributes are from different tables.
Pl suggest better options or improvise the current approaches I've thought of.

such that the values in B for 2nd table should be already present in 1st table's B attribute , & similarly values in C of 3rd table should be already present in C of 1st table.
In order to satisfy the first requirement, B must be declared as unique in the first table. To satisfy the second requirement, C must be unique in the first table. Thus, we would get a structure of (the choice of data type is arbitrary):
Create Table FirstTable
(
A varchar(50) not null Primary Key
, B varchar(50) Unique
, C varchar(50) Unique
);
Create Table SecondTable
(
B varchar(50)
, D varchar(50)
);
Alter Table SecondTable
Add Constraint FK_SecondTable_FirstTable_B
Foreign Key ( B )
References FirstTable ( B )
On Update Cascade;
Create Table ThirdTable
(
C varchar(50)
, E varchar(50)
);
Alter Table ThirdTable
Add Constraint FK_ThirdTable_FirstTable_C
Foreign Key ( C )
References FirstTable ( C )
On Update Cascade;
With respect to check constraints, one of the "cute" features of MySQL is that while it parses and accepts a check constraint in the Create Table statement, it completely ignores them with respect to evalution. To wit:
The CHECK clause is parsed but ignored by all storage engines.
Create Table documentation
Now, even if that were not the case, the Check constraint mechanism in the SQL language can only reference columns in the current table and thus would not help to solve your problem.

I'm not sure I fully understand the question. Would it not be sensible to have attributes B and C as Primary keys to Table 2, and 3 respectively. You could then reference them as foreign keys in the 1st table.

Related

mysql database foreign reference, delete not working

Hi I have a situation like shown below:
Here, I have two main tables A & B. with Primary Keys IdA and IdB.
Now I have a table C, where primary key is IdC, and colum IdB is actually a foreign reference to Table B (Id) with Cascade option.
Similarly, I have Table D with only two colums and both are references to table A and Table C.
So that IdA in D is referencing Id in Table A and IdC in table D is referencing IdC in Table C.
It works fine, such that if i delete an entry in Table C, it gets deleted from Table D.
Also if i delete an entry in Table A, it also gets deleted in Table D.
However what is not working, I can not delete an entry in Table D.
Atleast phpmyadmin does not allow me to do that. How can i do that. Because if i delete an entry in Table D. i don't wnat any other table to be affected by it. Any suggestions?
Okay so it was a very trivial issue, Table D did not have any primary key colum. After adding that, now i get the option to be able to delete any row i like

Using ON DELETE both ways, with a 1-1 relationship, where only table has a foreign key

Assume I have two tables, a and b. Table a contains an optional FOREIGN KEY reference to b. Table b does not have a FOREIGN KEY reference to table a, and should not. The reason for this is that table a is not the only table that might reference a row from table b: tables x and y might also reference b, and more tables might be added in the future which might reference a row from b.
So, every row from table b has exactly one "owner row" which may belong to a, x, y, or potentially any one of a number of other tables.
Now, if a row from table b is deleted, I want it to set the foreign key reference to itself from a, x, or y, if there is one, to null. I know I can accomplish this using ON DELETE SET NULL in the foreign key constraints for a and the other tables, so that is taken care of.
However, if the "owner row" is deleted, regardless of whether that row lives in a, x, y, or whatever, I want the corresponding row from b to be deleted as well. This is what I'm not sure how to do.
In short:
a references b. This is an optional reference, not all as will have a b.
(x, y, and other tables also have similar relationships to b)
b does not and should not reference a or any of those other tables.
If I delete from a and if the given a has a corresponding b, that b should be deleted.
If I delete from b, and any other table contains a reference to that b, the reference should be set to null.
How would I accomplish this?
create table a
( ...
, b_id bigint default null comment 'fk ref b.id'
, constraint a_b_id foreign key (b_id) reference b (id) on delete set null
, ...
) engine=innodb
tables x and y are defined similarly, with nullable foreign key columns
create table x
( ...
, b_id bigint default null comment 'fk ref b.id'
, constraint x_b_id foreign key (b_id) references b (id) on delete set null
, ...
) engine=innodb
and
create table y
( ...
, b_id bigint default null comment 'fk ref b.id'
, constraint y_b_id foreign key (b_id) references b (id) on delete set null
, ...
) engine=innodb
When a row is deleted from b, then any values of b_id column in any of the three tables references the deleted row, those values will be change to NULL.
There is no declarative constraint in MySQL that will accomplish 3.
"If I delete from a and if the given a has a corresponding b, that b should be deleted."
We might be able to accomplish this with a TRIGGER, but we get into some issues with which tables can be referenced by statements in the trigger. This would probably better be handled with the application logic, rather than a database rule or trigger.
If I was going to attempt a trigger, then something like
DELIMITER $$
CREATE TRIGGER a_ad
AFTER DELETE ON a
FOR EACH ROW
BEGIN
DELETE FROM b WHERE b.id = OLD.b_id ;
END$$
DELIMITER ;
(I'm not sure that this will be allowed, or if an error is going to be thrown... consider
table b
---------
row id=42
and
table a
-------
row id=2 b_id=42
row id=3 b_id=42
consider this SQL statement
DELETE FROM a WHERE a.id IN (2,3);
The delete of row id=2 is going to fire the "after delete" trigger; and that will perform a DELETE on b, the foreign key will find the row id=3 referencing, and attempt to set the b_id column to NULL... but that row might already be locked by the initial DELETE statement... I'm just not sure what will happen in this scenario; and we run into some limitations and restrictions on triggers (like against modifying rows in tables that are referenced in the statement that fires the trigger, )

The columns in 'table' do not match an existing primary key or unique constraint

Consider two database tables, tbl_One & tbl_Two.
tbl_One's primary keys are: A, B & E columns
tbl_Two's primary keys are: B & C.
I don't want any values in table tbl_Two column B that do not exist in table tbl_One column B.
Ideally, I want column B to be foreign key, but when I try to create this I get the following error message:
The columns in table 'tbl_One' do not match an existing primary key
or UNIQUE constraint.
How can I achieve this?
Caveat: Without more detail, I'm flying almost blind, my answer is based on what I could assume from your question.
If you really want to create a foreign key to a non-primary key, it MUST be a column that has a unique constraint on it.
From Books Online:
A FOREIGN KEY constraint does not have to be linked only to a PRIMARY
KEY constraint in another table; it can also be defined to reference
the columns of a UNIQUE constraint in another table.
In this instance, you would need column B in tbl_One to be unique, either by it being the primary key or having a Unique constraint on it.
UNIQUE constraint:
CREATE TABLE tbl_One
(
A int,
B int UNIQUE,
E int
CONSTRAINT [PK_tbl_One] PRIMARY KEY
(
A ASC,
B ASC,
E ASC
)
)
If it's the case that the data in column B is not unique, you can create a function based CHECK constraint to achieve a similar effect.
Function(Not tested):
CREATE FUNCTION dbo.CheckFunction(#B int)
RETURNS INT
AS BEGIN
RETURN (SELECT CASE COUNT(*)
WHEN 0 THEN 0
ELSE 1
END
FROM tbl_One
WHERE B=#B)
END
CHECK constraint:
ALTER TABLE tbl_Two
ADD CONSTRAINT chk_CheckFunction
CHECK (dbo.CheckFunction(B) = 1)
Here is a SQL Fiddle that demonstrates this.
Thanks for pointing out the referential integrity issue Damien, an AFTER DELETE trigger can help get around this, but there are shortcomings. Here is a good discussion on referential integrity managed with foreign keys vs triggers.
If you want a foreign key for field B in table B, remove field A, E 's primary key, create the relation then reset the primary keys back to those two fields.

How to make a relation between two tables without foreign key

I'm a total newbie and I'm trying to do a mysql database, using xampp for linux 1.8.1.
I want to make a relation between two tables A and B. For what I know foreign keys create a bijection, or a one-to-one relation. I must not have such a strict relation, so I only created a column inside table A that stores the id of table B.
Is this correct? There's not a way to enforce it? I mean, this way you could delete a row of table B that is referenced in table A. you could store a value inside A that doesn't correspond to an id of any row of B. How to prevent this?
The main problem for me is to prevent deletion of a row of table B if the row id is referenced by a row of table A.
create table table_b (
b_id integer primary key
);
create table table_a (
b_id integer primary key references table_b (b_id)
);
insert into table_b values (1);
insert into table_a values (1);
The following statement will fail.
delete from table_b where b_id = 1;
If you'd built that with PostgreSQL, the error message would say
ERROR: update or delete on table "table_b" violates foreign key constraint "table_a_b_id_fkey" on table "table_a" Detail: Key (b_id)=(1) is still referenced from table "table_a".
That structure gives you a "1 to 0 or 1" relationship between the two tables. For "1 to 0 or many", add one or more columns to table_a's primary key.
create table table_b (
b_id integer primary key
);
create table table_a (
b_id integer references table_b (b_id),
checkout_date date not null default current_date,
primary key (b_id, checkout_date)
);
That structure will let table_a store multiple rows for one value of b_id, but each of those rows must have a different checkout_date.
I think you allways need an primarykey for this, or you write a trigger who checks the consistence of ID from B when a change occurs.
I dont know that it would be possible without a trigger or a constraint ...

SQL auto-cleanup with 0..1:1..n tables in MySQL

I'm writing an application that requires all users to access data on a central database using MySQL, and I was wondering something.
Let's say I have this setup.
CREATE TABLE A
(
id INT PRIMARY KEY AUTO_INCREMENT,
data INT NOT NULL;
);
CREATE TABLE B
(
id INT PRIMARY KEY AUTO_INCREMENT,
a_id INT,
FOREIGN KEY (a_id) REFERENCES A(id) ON DELETE SET NULL
);
Now, the way I want this set up is, table A must ALWAYS be referenced by a row in table B. However, a row in table B may or may not reference a row in table A. The relationship is 1:n in that multiple rows in table B can reference a single row in table A. I am just wondering if it is possible to have the MySQL database automatically delete a row in A if it is no longer referenced by any row in table B.
The idea here is that I can simply set a_id in table B to NULL and have the database cleanup whatever is left. I guess that's similar to Java garbage collection now that I think about it. If there is no key to automatically enforce the constraint, would a trigger executed after an update work?
EDIT: Adding in the additional relationship constraint.
Run the following query at a specific interval:
DELETE tableA
FROM tableA LEFT JOIN tableB B ON A.id = B.a_id
WHERE B.a_id IS NULL;
Or, to maintain real-time consistency, you could create an OnChange trigger on tableB that performs similar.
Is there a reason for the table structure you are using? The way you are using foreign keys looks backwards to me. Instead of placing your key in table B, you could move it to table A.
This would give you a structure that looked more like:
tableA columns tableB columns
id
b_id id
[values] [values]
fk: a.b_id=b.id
A record added to table A would require a corresponding field in table B, but B would be free to have no values in table A. Then if you wanted to clean out the values in table A, you could simply use:
delete from tableA where b_id=[recordIdToNull];
You could even set A's foreign key up to cascade actions taken on B, so any values deleted from B would also delete the corresponding rows in A.