[英]#1452 - Cannot add or update a child row: a foreign key constraint fails
I am trying to insert some data into the table User
. 我试图将一些数据插入表User
。 The User
table has 2 foreign keys: StudentID
and StaffID
. User
表具有2个外键: StudentID
和StaffID
。
I want to be able to enter either StaffID
or StudentID
which should link to the relevant table(that already has either the StudentID
or StaffID
). 我希望能够输入StaffID
或StudentID
,它应该链接到相关的表(已经具有StudentID
或StaffID
)。 The table User
can only have StaffID
or StudentID
. 表User
只能具有StaffID
或StudentID
。 Can Anyone help? 有人可以帮忙吗?
INSERT INTO `User` (`UserName`, `Email`, `StudentID`,`StaffID`,`Paasword`)
VALUES ('phill', 'ph@lms.com', '', '2201','654321');
Ok, so prepare yourself. 好,准备好自己 This answer is long, but thorough. 这个答案很长,但是很彻底。
Short Answer: 简短答案:
As referenced in this answer by @HLGEM you can accomplish what you're asking by making the primary keys in the STAFF
and STUDENT
table, presumably the values StaffID
and StudentID
NULLABLE . 正如@HLGEM在此答案中所引用的,您可以通过在STAFF
和STUDENT
表中设置主键(假定值StaffID
和StudentID
NULLABLE)来完成您要的内容。 Here is the relevant snippet from his answer: 以下是他的回答的相关摘要:
To allow nulls in an FK generally all you have to do is allow nulls on the field that has the FK. 要在FK中允许空值,通常要做的就是在具有FK的字段上允许空值。 The null value is separate from the idea of it being an FK. 空值与其作为FK的想法是分开的。
You can do this in your table definition adding NULL
to your create statement similar to the following by specifying: 您可以在表定义中执行此操作,方法是指定以下内容,将NULL
添加到create语句中,类似于以下内容:
CREATE TABLE STUDENT (
StudentID INT UNSIGNED NULL PRIMARY KEY AUTO_INCREMENT
...
)
The above is an example of how to apply this to the STUDENT
table. 以上是如何将其应用于STUDENT
表的示例。 The STAFF
table would have a similar approach. STAFF
表将具有类似的方法。 Note, the extra values are there as a suggestion based on general configuration that is common when configuring ID
fields. 请注意,这些额外的值是根据配置ID
字段时常见的常规配置提供的建议。
LONG ANSWER: 长答案:
As @HLGEM mentioned in his answer, there are some times when it is appropriate to have a foreign key constraint that can be NULL
. 正如@HLGEM在他的回答中提到的,在某些情况下,具有可以为NULL
的外键约束是适当的。 However, in your case, it suggestions that the data is not fully normalized. 但是,根据您的情况,它建议数据未完全标准化。 The need for a NULL
foreign key can be eliminated in your case with a little table refactoring. 在您的情况下,只需进行少量表重构即可消除对NULL
外键的需要。 Let's explore an additional possibility when designing your database tables: 让我们探索设计数据库表时的另一种可能性:
Case Study: 案例分析:
Let's begin with the following assumption. 让我们从以下假设开始。 Since you said in your question: 由于您在问题中说过:
I want to be able to enter either StaffID or StudentID which should link to the relevant table(that already has either the StudentID or StaffID). 我希望能够输入StaffID或StudentID,它们应该链接到相关表(已经具有StudentID或StaffID)。 The table User can only have StaffID or StudentID 表用户只能具有StaffID或StudentID
It is probably a safe assumption to say that a user has to be a staff member or a student but not both. 可以肯定地说用户必须是工作人员 或 学生,但不能同时是两者。 That assumption makes a strong use case for having a UserType
table. 该假设为拥有UserType
表提供了强有力的用例。 Let's alter the USER
definition to support a UserTypeId
and create the USER_TYPE
table: 让我们更改USER
定义以支持UserTypeId
并创建USER_TYPE
表:
# USER TYPE Table Definition
CREATE TABLE USER_TYPES (
UserTypeId TINYINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
UserType VARCHAR(25) NOT NULL
) ENGINE=INNODB CHARSET=UTF8;
# USER TABLE Definition
CREATE TABLE USERS (
UserId INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(25) NOT NULL,
Email VARCHAR(50) NOT NULL,
Password VARCHAR(100) NOT NULL,
UserTypeId TINYINT UNSIGNED NOT NULL,
FOREIGN KEY(UserTypeId) REFERENCES USER_TYPES(UserTypeId)
) ENGINE=INNODB CHARSET=UTF8;
Notice that in this new schema, we no longer have a reference to StudentID
or StaffID
, but instead we have a UserTypeId
. 请注意,在此新架构中,我们不再引用StudentID
或StaffID
,而是使用UserTypeId
。 There are two benefits to this approach: 这种方法有两个好处:
USERS
. USERS
。 UserTypeId
is a foreign key reference to USER_TYPES
. UserTypeId
是对USER_TYPES
的外键引用。 UserTypeId
but no longer has to be NULLABLE . UserTypeId
但不再必须为NULLABLE 。 Student
or Staff
. 您不仅可以拥有Student
或Staff
还可以拥有更多的用户类型。 For our case study here, let's create two user types Student
and Employee
(I'll explain why I'm not using Staff
here in a little). 对于这里的案例研究,让我们创建两个用户类型Student
和Employee
(我将在这里解释为什么我不使用Staff
原因)。 Let's also go ahead and populate the USERS
table with the initial values for Phill
, your employee you mentioned in your question. 我们还继续用您在问题中提到的员工Phill
的初始值填充USERS
表。
INSERT INTO USER_TYPES(UserType)
VALUES("Employee"),("Student");
INSERT INTO USERS(Name,Email,Password,UserTypeId)
VALUES("phill","ph@lms.com","654321",1);
Excellent! 优秀的! Our new design is coming together quickly. 我们的新设计很快就会融合在一起。 Now let's create two additional tables, one called STUDENTS
and one called EMPLOYEES
. 现在让我们创建另外两个表,一个称为STUDENTS
,另一个称为EMPLOYEES
。 In this case, I chose to go with EMPLOYEES
instead of STAFF
because it allows you to be more flexible in how you define an employee . 在这种情况下,我选择使用EMPLOYEES
而不是STAFF
因为它使您在定义员工时更加灵活。 As you'll see in the definition, you can further define a user type of Employee
with an ENUM
value of either Faculty
, Staff
or Administrative
. 在定义中将看到,您可以进一步定义Employee
的用户类型,其ENUM
值为Faculty
, Staff
或Administrative
。 Think of this as a subtype of the general type Employee
. 将此视为一般类型Employee
的子类型。 Note that you could also create another join table like we did for USER_TYPES
, for instance one called EMPLOYEE_TYPES
. 请注意,您还可以像为USER_TYPES
那样创建另一个USER_TYPES
表,例如一个名为EMPLOYEE_TYPES
USER_TYPES
表。 Either method is appropriate. 两种方法都合适。 I chose to use an ENUM
instead of another foreign key to demonstrate an additional concept you could use if you only have a handful of choices. 我选择使用ENUM
而不是另一个外键来演示如果您只有少数选择的话可以使用的其他概念。
So on to those last two table definitions: 接下来是最后两个表定义:
# STUDENTS Table Definition
CREATE TABLE STUDENTS(
StudentId INT UNSIGNED PRIMARY KEY,
Year ENUM('Freshman','Sophmore','Junior','Senior') NOT NULL,
FOREIGN KEY(StudentId) REFERENCES USERS(UserId)
) ENGINE=INNODB CHARSET=UTF8;
# EMPLOYEES Table Definition
CREATE TABLE EMPLOYEES (
EmployeeId INT UNSIGNED PRIMARY KEY,
EmployeeType ENUM('Faculty','Staff','Administrative') NOT NULL,
FOREIGN KEY(EmployeeId) REFERENCES USERS(UserId)
) ENGINE=INNODB CHARSET=UTF8;
Notice that these two tables do not have their own Id
column, but instead, reference the Id
from the USERS
table as a foreign key constraint. 请注意,这两个表没有自己的Id
列,而是将USERS
表中的Id
作为外键约束进行引用。 This makes sense because you have to be a user before you can be a student or an employee . 这是有道理的,因为您必须先成为用户,然后才能成为学生或雇员 。
Finally, let's add some employee data for Phill
: 最后,让我们为Phill
添加一些员工数据:
INSERT INTO EMPLOYEES(EmployeeId,EmployeeType)
VALUES(1,"Faculty");
That was a lot, but now you'll begin to reap the benefits. 数量很多,但是现在您将开始获得好处。 Everything above was foundational in that it offers a fully normalized approach to your database layout with additional flexibility and without the need for a NULLABLE foreign key. 以上所有内容都是基础,因为它为您的数据库布局提供了完全标准化的方法,具有额外的灵活性,并且不需要NULLABLE外键。
Retrieving data in this instance is easy and we don't even have to know if Phill
is an employee or a student . 在这种情况下,检索数据很容易,我们甚至不必知道Phill
是雇员还是学生 。 Let's look at an example query: 让我们看一个示例查询:
SELECT
u.UserId,
u.Name,
u.Email,
ut.UserType,
s.*,
e.*
FROM USERS AS u
INNER JOIN USER_TYPES AS ut ON u.UserTypeId = ut.UserTypeId
LEFT JOIN STUDENTS AS s ON u.UserId = s.StudentId
LEFT JOIN EMPLOYEES AS e ON u.UserId = e.EmployeeId
WHERE u.Email = "ph@lms.com";
which returns: 返回:
+--------+--------+-------------+-----------+-----------+--------+------------+--------------+
| UserId | Name | Email | UserType | StudentId | Year | EmployeeId | EmployeeType |
+--------+--------+-------------+-----------+-----------+--------+------------+--------------+
| 1 | phill | ph@lms.com | Employee | (null) | (null) | 1 | Faculty |
+--------+--------+-------------+-----------+-----------+--------+------------+--------------+
Conclusion: 结论:
Live Example: sqlfiddle 实时示例: sqlfiddle
So there you have it. 所以你有它。 By performing a LEFT JOIN
between STUDENTS
, EMPLOYEES
and the USERS
table, you'll pull back all values that exist in either of the two tables and all the default user values. 通过在STUDENTS
, EMPLOYEES
和USERS
表之间执行LEFT JOIN
,您将拉回存在于两个表中的所有值以及所有默认用户值。 From here, it's a simple as checking for NULL
on StudentId
or EmployeeId
to determine which user type you're working with. 从这里开始,很简单,只需检查StudentId
或EmployeeId
上的NULL
即可确定您正在使用的用户类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.