foreign key referencing multiple tables - using multiple FKs - mysql

I have child table, A, which needs to refer to either of two different tables, B and C. B and C are similar but need to be in different tables.
As I understand it, mysql only allows a FK to refer to one table. Therefore, and having looked at other solutions, I've decided to create two columns in A to refer to either B or C. As it should only be B or C i've added in a constraint to prevent them both being NOT NULL:
CREATE TABLE conversions
(
id INT AUTO_INCREMENT,
kicker_id INT NOT NULL,
success BOOL NOT NULL,
try_id INT,
penalty_try_id INT,
PRIMARY KEY (id),
FOREIGN KEY (try_id),
FOREIGN KEY (penalty_try_id),
CONSTRAINT conversions_coll_null CHECK (try_id IS NULL OR penalty_try_id IS NULL)
);
Will this work? Is it a good design?
Thanks

This is a fine approach (assuming you add in the foreign key definitions), but with an important caveat: MySQL does not actually enforce check constraints. So, although you can include the constraint in the definition, it doesn't do anything.
If you want to insist on the constraint, then you need to use a trigger.
By the way, if you want to ensure that exactly one of the columns has a value, use XOR rather than OR. This would be expressed as:
CHECK (try_id IS NULL XOR penalty_try_id IS NULL)
(Or course, this doesn't do anything in MySQL, but it is just to show the correct logic.)

Related

Apply foreign key and check constraint to MySQL table

My question boils down to something like 'Can join table B be subject to a check constraint against a value in table A which is not a foreign key?'. The situation may be further complicated by multiple references to the same Foreign Key. The specific scenario I am struggling with is detailed below with some abbreviated MySQL code.
A MySQL database contains the table 'Disorder' (shown below):
CREATE TABLE `Disorder` (
disorder_name VARCHAR(255),
disorder_type VARCHAR(10),
PRIMARY KEY(`disorder_name`)
)
The disorder type can be either 'syndrome' or 'disease'. Diseases and syndromes can be linked in a many-to-many fashion (e.g. several diseases can be caused the same syndrome and the same disease may cause different syndromes).
I wish to create a join table called 'DiseaseSyndromeLink' showing the relationship between disorders that are 'diseases' and disorders that are 'syndromes'.
CREATE TABLE `DiseaseSyndromeLink` (
`ds_id` int NOT NULL AUTO_INCREMENT,
`disease` VARCHAR(255) NOT NULL,
`syndrome` VARCHAR(255) NOT NULL,
PRIMARY KEY (`ds_id`),
FOREIGN KEY (disease) REFERENCES disorder(disorder_name),
FOREIGN KEY (syndrome) REFERENCES disorder(disorder_name)
)
This table needs constraints that are tricky to design:
The first column is a integer join ID
The second column 'Disease' is a foreign key referencing Disorder.disorder_name. Only disorder_names where Disorder.disorder_type='Disease' should be allowed to be entered here.
The third column 'Syndrome' is a foreign key referencing Disorder.disorder_name. Only disorder_names where Disorder.disorder_type='Syndrome' should be allowed to be entered here.
I feel the syntax should be something like:
CREATE TABLE `DiseaseSyndromeLink` (
`ds_id` int NOT NULL AUTO_INCREMENT,
`disease` VARCHAR(255) NOT NULL,
`syndrome` VARCHAR(255) NOT NULL,
PRIMARY KEY (`ds_id`),
FOREIGN KEY (disease) REFERENCES disorder(disorder_name) WHERE (Disorder.disorder_type='Disease'),
FOREIGN KEY (syndrome) REFERENCES disorder(disorder_name) WHERE (Disorder.disorder_type='Syndrome')
)
My understanding is that checking values (e.g. ='Disease') requires a check constraint whereas linking to the original table requires a foreign key constraint. I cannot find any docummentation or YouTube tutorials detailing using BOTH these constraints simultaneously. As it has been very hard to find any examples of this code I wondered whether I have made a mistake with respect to database design but cannot think of a good alternative.
Can check and foreign key constraints be used together like this?
Thanks for your time!
QUESTION EDITED AS ORIGINALLY CONTAINED INFORMATION ABOUT TRYING TO ENFORCE THIS RELATIONSHIP AT THE DJANGO SIDE.
As I see the your MYSQL statement kinda worked, so all you need to do is add foreign key in the same way to the model like you did. Although this time, you will set 'null=True' and if you have unique values then 'unique=True' this will take care of empty columns. Now you have to implement rest in application logic while adding data, you have to be aware 'where disease type' , etc criteria. But when you are reading from the database Django will automatically prefetch related. Example query for reading data.
data=Diseasesyndromelink.objects.filter(disease__feild="query")
print(data.disease) #your foreign key object is prefetched here already.
Check out the documentation for more examples.
https://docs.djangoproject.com/en/3.0/topics/db/models/

MySQL: Is it possible to make a foreign key to two different table-columns?

I have a table (a) which should have a column "user_id", which is a foreign key of some other table (b) OR (c).
So wether it points to table b or c (only one, not both).
Is this even possible in MySQL? Best thing I can come up with is following (but as you can see, I have "user_id" twice in my "a"-table, which is not very nice, since one of the two values will always be empty.
Thank you very much!
Hi this is called Polymorphic Associations and you cannot enforce it using MySql foreign key constraints.
in order to have either userId_a null or user_id_b null you can create a check constraint
CONSTRAINT CHECK (user_id_a IS NOT NULL OR user_id_b IS NOT NULL)

complex mysql constraints over foreign keys

It seems rational to me to stop users or bad codes from inserting invalid data, but I don't remember to see this anywhere!
Consider the following tables
How I can make sure an order is always referencing an address that is created by the same user?
Is this kind of constraint usual and recommended? I mean, Do I even have to care about it in the design?
Since I would not expect a user to be able to place an order without a valid address, therefore I would simply remove the separate FK to the user table, and use the combined user id - address id fields from the address table as a foreign key.
CREATE TABLE orders AS (
--[COLUMN DEFINITIONS]
address_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
CONSTRAINT fk_usr_addr FOREIGN KEY (user_id, address_id)
REFERENCES address(user_id, id)
) ENGINE=InnoDB;
If an order is incomplete and does not have an address yet, then this should not be an issue for a multi column foreign key, since according to mysql documentation on using foreign keys:
The MATCH clause in the SQL standard controls how NULL values in a
composite (multiple-column) foreign key are handled when comparing to
a primary key. MySQL essentially implements the semantics defined by
MATCH SIMPLE, which permit a foreign key to be all or partially NULL.
In that case, the (child table) row containing such a foreign key is
permitted to be inserted, and does not match any row in the referenced
(parent) table. It is possible to implement other semantics using
triggers.

Can a MySQL InnoDB database have a foreign column uniqueness constraint?

I am using a MySQL InnoDB database and have many tables in it. What I want to be able to do is enforce (from within the database) a constraint such that a key may exist in one of two columns (in two separate tables) but not both. I'll try to make this more clear.
Say I have two tables, TableA and TableB. Both of these tables have many columns, but they have one column in common, called SpecialID (int 255).
Now, both of these tables have many rows, and from the PHP side of the web app, the SpecialID column in TableA should never contain an integer that is in the SpecialID column of TableB, and the same goes the other way around. In other words, an integer should never be able to be found in the SpecialID column of TableA and TableB at any one time.
I'm fairly confident that I've enforced this from the PHP side, however I want to be able to enforce this relationship from within the database, just to be extra careful, as if I ever ended up with the same value in both tables, it would be catastrophic.
This may not even be possible, but I thought I'd throw it out there cos it seems like it could be. It would be sort of like a "foreign uniqueness constraint". I have done a bit of research but haven't turned up anything at all, not even people asking for something like this, so perhaps I could just be searching for the wrong thing?
Here's a solution:
CREATE TABLE Specials (
specialid INT AUTO_INCREMENT PRIMARY KEY,
type CHAR(1) NOT NULL,
UNIQUE KEY (id, type)
);
CREATE TABLE TableA (
id INT AUTO_INCREMENT PRIMARY KEY,
specialid INT NOT NULL,
type CHAR(1) NOT NULL DEFAULT 'A',
FOREIGN KEY (specialid, type) REFRENCES Specials(specialid, type)
);
CREATE TABLE TableB (
id INT AUTO_INCREMENT PRIMARY KEY,
specialid INT NOT NULL,
type CHAR(1) NOT NULL DEFAULT 'B',
FOREIGN KEY (specialid, type) REFRENCES Specials(specialid, type)
);
Now you need to make sure TableA.type is always 'A' and TableB.type is always 'B'. You can do this with a trigger, or else a foreign key to a lookup table of one row for each case.
The result is that Specials.type can be any letter, but only one letter for a given specialid. The rows in TableA and TableB can reference only a specialid with a type that matches their own type. This means that any given specialid can be referenced by only one table or the other, but never both.
From what I've found, there is no easy way to do this. As I cannot find anything and no one has provided a solution to the issue, I will assume that it is in fact not possible (at least, not very easily).
For my own project, I ended up going along a different route to achieve my goal.
The discussion at this SO question may be of use for anyone searching for something along these lines:
Enforce unique values across two tables
I have not tried it myself, though.
If I ever come across anything better (or someone posts a better answer here) then I shall update my response and/or mark someone else's answer as correct as necessary.

MySQL InnoDB - Constraints for keys linking parent to one of two children

Using MySQL, moving from MyISAM to InnoDB tables. Database design started with dumping the data and re-importing it without foreign keys or constraints. Adding those one at a time to find errors.
I have ParentTable which can either be linked to ChildTableA or ChildTableB, but not both. Should the CREATE syntax be: (using CREATE syntax for simplicity rather than multiple ALTERs)
CREATE TABLE `ParentTable`
`IDParentTable` bigint(20) unsigned NOT NULL auto_increment,
`IDChildTableA` bigint(20) unsigned NOT NULL default '0',
`IDChildTableB` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`IDParentTable`),
KEY `ParentTable_IDChildTableA` (`IDChildTableA`),
KEY `ParentTable_IDChildTableB` (`IDChildTableB`)
ENGINE=InnoDB DEFAULT CHARSET=latin1;
Without thinking about it, I tried including:
CONSTRAINT `ParentTable_IDChildTableA` FOREIGN KEY (`IDChildTableA`) REFERENCES `ChildTableA` (`IDChildTableA`),
CONSTRAINT `ParentTable_IDChildTableB` FOREIGN KEY (`IDChildTableB`) REFERENCES `ChildTableB` (`IDChildTableB`)
Which failed, because many rows have 0 for IDChildTableA, and many rows have 0 for IDChildTableB. But, no rows have 0 for both. It's seeing that no ChildTableA exists with IDChildTableA of 0, and likewise with B.
Is there a proper way to handle this situation while keeping referential integrity? Without splitting ParentTable in two? A way to say it's OK if it's 0 or references a valid related table? Or, does wanting polymorphic tables mean I have to go without constraints?
BTW, I much prefer this route than having a single IDChildTable foreign key and then having another column designating whether it's table A or B... Not how I see that would work either for constraints, just saying I prefer not to go that route...
A column used as a foreign key can be nullable. Use a NULL value in the foreign key column to indicate "no row referenced."
It seems like you have your foreign keys backwards. Usually, the child table has a reference to the parent table.
parent (id int primary key)
childA (id int, parent_id int, ...)
childB (id int, parent_id int, ...)
EDIT
Related to the question regarding a foreign key column referencing two tables (based on a discriminator column)... that's not possible. A foreign key constraint can reference only one table.
To get something like that work, you'd need to add two separate foreign key columns, each referencing one target table. You could make use of the extra discriminator column (A or B) to identify which foreign key column should be used, so one fk column would be populated with a reference, the other fk column would be set to NULL.
However, there is no declarative constraint that would require exactly one of those two fk columns to be populated. That would not be enforced by the database. The extra discriminator column would actually be redundant, because you could derive that based on which foreign key column was populated.