SQL auto-cleanup with 0..1:1..n tables in MySQL - 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.

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

Create Foreign key from a primary key

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.

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

Populate table with all foreign keys from other table's primary key

I'm working on a Django application and created a new table tblA which has a foreign key that is linked to the primary key of tblB.
Now tblB already has several entries (and thus several primary keys in it already).
I want to run an SQL query that will create a new row for every primary key in tblA inside tblB with the corresponding foreign key copied and default values of all other columns in tblB inserted in the rows.
I hope I was clear enough!
you have forgot to paste your table structure .. I am considering as below-
tblA
(
aCol1_PK (primary key),
acol2_desc (description)
)
as of now just take an example of two columns only.
Now for table B
tblB
(
bCol1,
bCol2,
bCol3_FK (foreign key)
)
Now let say you have default values as -
bCol1 - "B_Col1_default_val"
bcol2 - "B_Col2_default_val"
for this situation if you want to insert rows for p_key from table A which is not present in Table B, you can try below query -
insert into tblB(col1,col2,col3)
( select 'b_col_default_val','b_col_default_val', acol1_pk
from tbla a
where a.acol1_pk not in ( select b.bcol3_fk from tblb b))
if you want to insert one row for all the primary key value present in table A then -
insert into tblB(col1,col2,col3)
( select 'b_col_default_val','b_col_default_val', acol1_pk
from tbla a )
I hope it will help you ..
if you require any clarification you can ask/comment.