Is it possible to reference one column as multiple foreign keys?
No.
That is, you cannot create a foreign key constraint this way. You can however, use a foreign key without a foreign key constraint.
All a foreign key is, is the value of another table's (or another record in the same table) primary key, which can be used in joins. In fact, you could reference fields other than the primary key, if all you need is to use the value for joins.
However, a foreign key constraint tells the database to enforce the rule that for every foreign key value in a table, the referenced table has a record with that as it's primary key. Enforcing that every foreign key in the PDF table had a primary key IN ALL FOUR TABLES won't work for you. So go ahead and use the field to reference other records, but simply do not create any foreign key constraint.
Multiple foreign keys to a single column
No, you can't have a single field as a foreign key to two different tables. How would you tell where to look for the key?
You would at least need a field that tells what kind of user it is, or two separate foreign keys.
You could also put the information that is common for all users in one table and have separate tables for the information that is specific for the user types, so that you have a single table with user id as primary key.
Using the same column in multiple foreign keys
you can't change to lectureids 2 because none exists in the first place in the table
And the cascade works the other way
Further GROUPS os a reserved word in mysql, and should so be avoided.
Last i had to remove the second foreign key, as it already exists in groups and is so unnecessary, as the fpoeign key in groups in groups already chekcs if such a lectureid exists
EXAMPLE for the working of foreign keys
CREATE TABLE lectures (
lectureId INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId)
);
✓
CREATE TABLE `groups` (
lectureId INT NOT NULL,
groupNo INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId,groupNo),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE
);
✓
CREATE TABLE studentListed (
studentId INT NOT NULL,
lectureId INT NOT NULL,
groupNo INT NULL,
PRIMARY KEY (studentId,lectureId),
#FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
# ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (lectureId,groupNo) REFERENCES `groups` (lectureId,groupNo)
ON UPDATE CASCADE ON DELETE CASCADE
);
✓
CREATE TRIGGER GroupDelete BEFORE DELETE ON `groups`
FOR EACH ROW
UPDATE studentListed SET studentListed.groupNo = NULL
WHERE studentListed.lectureId = OLD.lectureId
AND studentListed.groupNo = OLD.groupNo;
✓
INSERT INTO lectures
VALUES
(1, "lecture1");
✓
INSERT INTO `groups`
VALUES
(1, 1, "group1");
✓
INSERT INTO studentListed
VALUES
(1, 1, 1);
✓
UPDATE lectures SET lectureId = 2 WHERE lectureId = 1
✓
SELECT * FROM studentListed
studentId | lectureId | groupNo
--------: | --------: | ------:
1 | 2 | 1
UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */
✓
✓
db<>fiddle here
yu can't build that in t your way you have to break up the primary key from Groups and reference only groups with it, like
CREATE TABLE lectures (
lectureId INT NOT NULL,
title VARCHAR(10) NOT NULL,
PRIMARY KEY (lectureId)
);
✓
CREATE TABLE `groups` (
lectureId INT NOT NULL,
groupNo INT NOT NULL PRIMARY KEY,
title VARCHAR(10) NOT NULL,
UNIQUE KEY (lectureId,groupNo),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE
);
✓
CREATE TABLE studentListed (
studentId INT NOT NULL,
lectureId INT NOT NULL,
groupNo INT NULL,
PRIMARY KEY (studentId,lectureId),
FOREIGN KEY (lectureId) REFERENCES lectures (lectureId)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (groupNo) REFERENCES `groups` (groupNo)
ON UPDATE CASCADE ON DELETE CASCADE
);
✓
CREATE TRIGGER GroupDelete BEFORE DELETE ON `groups`
FOR EACH ROW
UPDATE studentListed SET studentListed.groupNo = NULL
WHERE studentListed.lectureId = OLD.lectureId
AND studentListed.groupNo = OLD.groupNo;
✓
INSERT INTO lectures
VALUES
(1, "lecture1");
✓
INSERT INTO `groups`
VALUES
(1, 1, "group1");
✓
INSERT INTO studentListed
VALUES
(1, 1, 1);
✓
UPDATE lectures SET lectureId = 2 WHERE lectureId = 1
✓
SELECT * FROM studentListed
studentId | lectureId | groupNo
--------: | --------: | ------:
1 | 2 | 1
UPDATE lectures SET lectureId=2 WHERE lectureId=1; /* Offending line */
✓
✓
DELETE FROM lectures WHERE lectureID = 2
✓
SELECT * FROM studentListed
studentId | lectureId | groupNo
--------: | --------: | ------:
db<>fiddle here
this will need still more improvment as your concept egts more complicated
Create a view with multiple foreign key referencing a single field
This is a common reporting pattern wherever the database architect has employed the "one true lookup table" model. I'm not going to get bogged down in the merits of that design. People like Celko and Phil Factor are far more erudite than me at commenting on these things. All I'll say is that having reported off over sixty enterprise databases in the last 15 years, that design is pervasive. Rightly or wrongly, you're probably going to see it over and over again.
There is currently insufficient information to definitively answer your question. The answer below makes assumptions on what I think is the most likely missing information is.
- I'll assume your product table is named PRODUCT
- I'll assume your all-powerful lookup table is call REFS
- I'll assume RefCodeKey in REFS has a unique constraint on it, or it is the a primary key
- I'll assume the REFS table is relatively small (say < 100,000 rows). I'll come back to this point later.
I'll assume that the foreign keys in the PRODUCT table are nullable. This affects whether we INNER JOIN or LEFT JOIN.
SELECT prod.PC
,prod.PN
,reg_code.label as RCKey
,prod_stat.label as PSKey
,prod_clas.label as PCKey
FROM PRODUCT prod
LEFT JOIN REFS reg_code ON prod.RCKey = reg_code.RefCodeKey
LEFT JOIN REFS prod_stat ON prod.PSKey = prod_stat.RefCodeKey
LEFT JOIN REFS prod_clas ON prod.PCKey = prod_clas.RefCodeKey
;
The trick is that you can refer to the REFS table as many times as you like. You just need to give it a different alias and join it to the relevant FK each time. For example reg_code
is an alias. Give your aliases meaningful names to keep your code readable.
Note: Those RCKey/PSKey/PCKey names are really not good names. They'll come back to bite you. They don't represent the key. They represent a description of the thing in question. If it's a region code, call it region_code
The reason I'm assuming the REFS table is relatively small, is that if it's really large (I've seen one with 6 million lookup values across hundreds of codesets) and indexed to take RefCodeType into consideration, you might get better performance by adding a filter for RefCodeType to each of your LEFT JOINs. For example:
LEFT JOIN REFS prod_clas ON prod.PCKey = prod_clas.RefCodeKey
AND prod_clas.RefCodeType = 'ProductClassificationKey'
Two Foreign Keys on one column
No. You cannot have multiple foreign keys on the same column. From the MySQL documentation:
MySQL supports foreign key references between one column and another within a table. (A column cannot have a foreign key reference to itself.) In these cases, “child table records” really refers to dependent records within the same table.
The reason for this is that MySQL won't be able to differentiate between the parents. Laravel (or any other framework) does not provide a work around for this issue.
This question as been asked in slightly different forms before. Example: it is possible to reference one column as multiple foreign keys
Multiple foreign keys referencing same table column in SQLAlchemy
The assumption is that in your scenario a user can rate other users, and they (themselves) can be rated. Basically, there is one table called User
referencing other users within an application.
A relationship in which instances of a class are linked to other instances of the same class is called self-referential relationship, and that is exactly what you have here.
Here is a diagram that represents this self-referential many-to-many relationship that keeps track of ratings:
The Ratings
table is the association table of the relationship. The foreign keys in this table are pointing at entries in the User
table since it is linking users to users.
To add this table to your database, this is how you can go about it:
ratings = db.Table('ratings'
db.Column('my_ratings_id', db.Integer, db.ForeignKey('user.id'))
db.Column('other_people_rating_id', db.Integet, db.ForeignKey('user.id'))
)
This is an auxiliary table (directly-translated as seen above) that has no data other than foreign keys. It is, therefore, created without an associated model class.
To declare the many-to-many relationship in the User
table, add this:
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), unique=True, nullable=False)
def __repr__(self):
return f'{self.username}'
my_ratings = db.relationship(
'User',
secondary=ratings,
primaryjoin=(ratings.c.my_ratings_id == id),
secondaryjoin=(ratings.c.other_people_rating_id == id),
backref = db.backref('other_people_rating', lazy='dynamic'), lazy='dynamic'
)
I am defining the relationship as seen from the left side user with the name my_ratings
because when you query this relationship from the left side, you will get a list of all those on the right side. Visually, this is what I mean:
Examining all the arguments of the db.relationship()
call, you will see that:
User
is the right side entity of the relationship.secondary
configures theratings
association tableprimaryjoin
indicates the condition that links the left side entity with the association table. Theuser id
should matchmy_ratings_id
secondaryjoin
indicates the condition that links the right side entity with the association table. Again,other_people_rating_id
should match theuser
idbackref
defines how this relationship will be accessed from the right side entity. From the left side, the relationship is namedmy_ratings
, so from the right side, I decided to name itother_people_rating
to represent all the left side users that are linked to the target user in the right side.- The
dynamic
mode is used to set up the query to not run until specifically requested. - The second
lazy
parameter applies to the left side query instead of the right side.
One foreign key with multiple columns VS multiple foreign keys with single column
My question: are SQL code 1 and SQL code 2 equal?
Not, they are not equivalent.
By the definition
Foreign Key Constraints
A foreign key constraint (also called a referential integrity
constraint) designates a column as the foreign key and establishes a
relationship between that foreign key and a specified primary or
unique key, called the referenced key. A composite foreign key
designates a combination of columns as the foreign key.
According to the above this declaration:
CONSTRAINT FK_E
FOREIGN KEY (E1, E2, E3)
REFERENCES F (E1, E2, E3),
assumes that there is either a primary key or unique constraint created on F
table
CREATE TABLE F(
.....
.....
CONSTRAINT my_pk PRIMARY KEY(E1, E2, E3)
)
while this declaration
CONSTRAINT FK_E1
FOREIGN KEY (E1)
REFERENCES F (E1),
CONSTRAINT FK_E2
FOREIGN KEY (E2)
REFERENCES F (E2),
CONSTRAINT FK_E3
FOREIGN KEY (E3)
REFERENCES F (E3),
is in a need of existence of three constraints, either primary key or unique indexses/constraints:
CREATE TABLE F(
.....
.....
CONSTRAINT my_pk1 PRIMARY KEY(E1),
CONSTRAINT my_uq2 UNIQUE(E2),
CONSTRAINT my_uq3 UNIQUE(E3)
)
Note 1- the table can only have one primary key, so only one constraint in your example could be the primary key, the rest 2 (or all 3) must be unique keys.
Note 2 - there is a slight semantic difference between the primary key constraint and the unique key constraint. The primary key values must be unique and must not contain null values while the unique key values can be NULL.
In the first case the table F
can contain these values
E1 E2 E3
1 1 1
1 1 2
2 2 1
and the child table can contain only these records:
E1 E2 E3
1 1 1
1 1 2
2 2 1
but you cannot insert to the child table these combination of values because they don't exists in the parent table:
E1 E2 E3
1 2 1
2 2 2
In the secondcase the table F
can contain these values
E1 E2 E3
1 1 1
2 2 2
3 3 3
but cannot contain these values, because each column must be unique:
E1 E2 E3
1 1 1
1 1 2
2 1 3
while the child table can contain these records:
E1 E2 E3
1 2 3
3 1 2
2 1 3
Are multiple foreign keys in a single field possible?
What you typically do is set up a many to many relationship with an intermediate linking table. Some thing like the following:
CREATE TABLE product (
`id` integer AUTO_INCREMENT NOT NULL,
-- other cols --
PRIMARY KEY (`id`)
);
CREATE TABLE certification (
`id` integer AUTO_INCREMENT NOT NULL,
-- other cols --
PRIMARY KEY (`id`)
);
CREATE TABLE product_certification (
`product_id` integer NOT NULL,
`certification_id` integer NOT NULL,
PRIMARY KEY (`product_id`, `certification_id`),
CONSTRAINT `product_id_product_id`
FOREIGN KEY (`product_id`)
REFERENCES `product` (`id`) ON DELETE CASCADE,
CONSTRAINT `certification_id_certification_id`
FOREIGN KEY (`certification_id`)
REFERENCES `certification` (`id`) ON DELETE CASCADE
);
Related Topics
How to Get Multiple Rows into One Line as a String
Isdate Function in SQL Evaluates Invalid Dates as Valid
How to Query Nested Arrays in a Postgres JSON Column
One-To-Many Query Selecting All Parents and Single Top Child for Each Parent
SQL Select Multi-Columns into Multi-Variable
Group Data by the Change of Grouping Column Value in Order
How to Delete Multiple Rows with Different Ids
How to Get Week Start and End Date String in Postgresql
How to Change the Date Format from Mm/Dd/Yyyy to Yyyy-Mm-Dd in Pl/Sql
How to Count Rows That Have the Same Values in Two Columns (Sql)
Fast Relational Method of Storing Tree Data (For Instance Threaded Comments on Articles)
Postgresql Not Ilike Clause Does Not Include Null String Values
How to Query for Null Values in JSON Field Type Postgresql
Sequentially Number Rows by Keyed Group in SQL
Quickest Way to Fill SQL Table with Dummy Data
Update or Insert (Multiple Rows and Columns) from Subquery in Postgresql