简体   繁体   中英

#1452 - Cannot add or update a child row: a foreign key constraint fails

I am trying to insert some data into the table User . The User table has 2 foreign keys: StudentID and 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 ). The table User can only have StaffID or 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 . 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. The null value is separate from the idea of it being an FK.

You can do this in your table definition adding NULL to your create statement similar to the following by specifying:

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. The STAFF table would have a similar approach. Note, the extra values are there as a suggestion based on general configuration that is common when configuring ID fields.

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 . 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. 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). The table User can only have StaffID or 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. Let's alter the USER definition to support a UserTypeId and create the USER_TYPE table:

# 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 . There are two benefits to this approach:

  1. USERS . UserTypeId is a foreign key reference to USER_TYPES . UserTypeId but no longer has to be NULLABLE .
  2. You can have more user types than just Student or 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). Let's also go ahead and populate the USERS table with the initial values for Phill , your employee you mentioned in your question.

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 . 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 . 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 . Think of this as a subtype of the general type Employee . Note that you could also create another join table like we did for USER_TYPES , for instance one called EMPLOYEE_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.

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

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.

Retrieving data in this instance is easy and we don't even have to know if Phill is an employee or a student . 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

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. From here, it's a simple as checking for NULL on StudentId or EmployeeId to determine which user type you're working with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM