vấn đề vi phạm ràng buộc khóa ngoại


10

Tôi đã xác định được 3 tình huống.

  1. Một sinh viên không có tuyển sinh.
  2. Một sinh viên với tuyển sinh nhưng không có điểm.
  3. Một sinh viên với tuyển sinh và điểm.

Có một kích hoạt trên bảng ghi danh để tính điểm trung bình. Nếu một học sinh có điểm, nó sẽ cập nhật hoặc chèn một mục vào bảng GPA; không có điểm, không có bảng điểm GPA.

Tôi có thể xóa một sinh viên không đăng ký (# 1). Tôi có thể xóa một học sinh với số lượng đăng ký và điểm số (# 3 ở trên). Nhưng tôi không thể xóa một học sinh có ghi danh nhưng không có điểm số (# 2). Tôi nhận được một vi phạm ràng buộc tham chiếu.

Câu lệnh DELETE đã xung đột với ràng buộc TÀI LIỆU THAM KHẢO "FK_dbo.GPA_dbo.Student_StudentID". Xung đột xảy ra trong cơ sở dữ liệu "", bảng "dbo.GPA", cột 'StudentID'.

Nếu tôi không thể xóa một sinh viên mới không đăng ký (và không có điểm trung bình GPA) thì tôi sẽ hiểu vi phạm ràng buộc, nhưng tôi có thể xóa sinh viên đó. Đó là một sinh viên có số lượng đăng ký và không có điểm (và vẫn không có điểm GPA) mà tôi không thể xóa.

Tôi đã vá kích hoạt của tôi để tôi có thể đi về phía trước. Bây giờ, nếu bạn đã đăng ký, trình kích hoạt sẽ đưa bạn vào bảng GPA bất kể điều gì. Nhưng tôi không hiểu vấn đề tiềm ẩn. Bất kỳ lời giải thích sẽ được đánh giá cao nhất.

Cho những gì nó có giá trị:

  1. Visual Studio 2013 chuyên nghiệp.
  2. IIS express (nội bộ đến VS2013).
  3. Ứng dụng web ASP.NET sử dụng EntityFramework 6.1.1.
  4. Máy chủ MS SQL 2014.
  5. GPA.Value là nullable.
  6. Ghi danh.GradeID là nullable.

Đây là một đoạn của cơ sở dữ liệu:

hình ảnh cơ sở dữ liệu

- CHỈNH SỬA -

Tất cả các bảng được tạo bởi EntityFramework, tôi đã sử dụng SQL Server Management Studio để sản xuất chúng.

Dưới đây là các câu lệnh tạo bảng với các ràng buộc.:

GPA bàn:

CREATE TABLE [dbo].[GPA](
    [StudentID] [int] NOT NULL,
    [Value] [float] NULL,
  CONSTRAINT [PK_dbo.GPA] PRIMARY KEY CLUSTERED 
  (
    [StudentID] ASC
  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
         ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[GPA]  WITH CHECK 
  ADD  CONSTRAINT [FK_dbo.GPA_dbo.Student_StudentID] 
  FOREIGN KEY([StudentID])
  REFERENCES [dbo].[Student] ([ID])

ALTER TABLE [dbo].[GPA] 
  CHECK CONSTRAINT [FK_dbo.GPA_dbo.Student_StudentID]

Enrollment bàn:

CREATE TABLE [dbo].[Enrollment](
    [EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
    [CourseID] [int] NOT NULL,
    [StudentID] [int] NOT NULL,
    [GradeID] [int] NULL,
  CONSTRAINT [PK_dbo.Enrollment] PRIMARY KEY CLUSTERED 
  (
    [EnrollmentID] ASC
  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
         ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[Enrollment]  WITH CHECK 
  ADD  CONSTRAINT [FK_dbo.Enrollment_dbo.Course_CourseID] 
  FOREIGN KEY([CourseID])
  REFERENCES [dbo].[Course] ([CourseID])
  ON DELETE CASCADE

ALTER TABLE [dbo].[Enrollment] 
  CHECK CONSTRAINT [FK_dbo.Enrollment_dbo.Course_CourseID]

ALTER TABLE [dbo].[Enrollment]  WITH CHECK 
  ADD  CONSTRAINT [FK_dbo.Enrollment_dbo.Grade_GradeID] 
  FOREIGN KEY([GradeID])
  REFERENCES [dbo].[Grade] ([GradeID])

ALTER TABLE [dbo].[Enrollment] 
  CHECK CONSTRAINT [FK_dbo.Enrollment_dbo.Grade_GradeID]

ALTER TABLE [dbo].[Enrollment]  WITH CHECK 
  ADD  CONSTRAINT [FK_dbo.Enrollment_dbo.Student_StudentID] 
  FOREIGN KEY([StudentID])
  REFERENCES [dbo].[Student] ([ID])
  ON DELETE CASCADE

ALTER TABLE [dbo].[Enrollment] 
  CHECK CONSTRAINT [FK_dbo.Enrollment_dbo.Student_StudentID]

Student bàn:

CREATE TABLE [dbo].[Student](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [EnrollmentDate] [datetime] NOT NULL,
    [LastName] [nvarchar](50) NOT NULL,
    [FirstName] [nvarchar](50) NOT NULL,
  CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED 
  (
    [ID] ASC
  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
         ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Dưới đây là các kích hoạt :

CREATE TRIGGER UpdateGPAFromUpdateDelete
ON Enrollment
AFTER UPDATE, DELETE AS
BEGIN
    DECLARE @UpdatedStudentID AS int
    SELECT @UpdatedStudentID = StudentID FROM DELETED
    EXEC MergeGPA @UpdatedStudentID
END

CREATE TRIGGER UpdateGPAFromInsert
ON Enrollment
AFTER INSERT AS
--DECLARE @InsertedGradeID AS int
--SELECT @InsertedGradeID = GradeID FROM INSERTED
--IF @InsertedGradeID IS NOT NULL
    BEGIN
        DECLARE @InsertedStudentID AS int
        SELECT @InsertedStudentID = StudentID FROM INSERTED
        EXEC MergeGPA @InsertedStudentID
    END

Các bản vá để di chuyển về phía trước là để nhận xét những dòng trong AFTER INSERTkích hoạt.

Đây là thủ tục được lưu trữ :

CREATE PROCEDURE MergeGPA @StudentID int AS
MERGE GPA AS TARGET
USING (SELECT @StudentID) as SOURCE (StudentID)
ON (TARGET.StudentID = SOURCE.StudentID)
WHEN MATCHED THEN
    UPDATE
        SET Value = (SELECT Value FROM GetGPA(@StudentID))
WHEN NOT MATCHED THEN
INSERT (StudentID, Value)
    VALUES(SOURCE.StudentID, (SELECT Value FROM GetGPA(@StudentID)));

Đây là chức năng cơ sở dữ liệu :

CREATE FUNCTION GetGPA (@StudentID int) 
RETURNS TABLE
AS RETURN
SELECT ROUND(SUM (StudentTotal.TotalCredits) / SUM (StudentTotal.Credits), 2) Value
    FROM (
        SELECT 
            CAST(Credits as float) Credits
            , CAST(SUM(Value * Credits) as float) TotalCredits
        FROM 
            Enrollment e 
            JOIN Course c ON c.CourseID = e.CourseID
            JOIN Grade g  ON e.GradeID = g.GradeID
        WHERE
            e.StudentID = @StudentID AND
            e.GradeID IS NOT NULL
        GROUP BY
            StudentID
            , Value
            , e.courseID
            , Credits
    ) StudentTotal

Đây là đầu ra gỡ lỗi từ phương thức xóa của bộ điều khiển, câu lệnh select là phương thức truy vấn những gì cần xóa. Học sinh này có 3 lần đăng ký, REFERENCEvấn đề ràng buộc xảy ra khi đăng ký thứ 3 xóa. Tôi cho rằng EF đang sử dụng một giao dịch vì các đăng ký không bị xóa.

iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0004945;Properties:
Command: SELECT 
    [Project2].[StudentID] AS [StudentID], 
    [Project2].[ID] AS [ID], 
    [Project2].[EnrollmentDate] AS [EnrollmentDate], 
    [Project2].[LastName] AS [LastName], 
    [Project2].[FirstName] AS [FirstName], 
    [Project2].[Value] AS [Value], 
    [Project2].[C1] AS [C1], 
    [Project2].[EnrollmentID] AS [EnrollmentID], 
    [Project2].[CourseID] AS [CourseID], 
    [Project2].[StudentID1] AS [StudentID1], 
    [Project2].[GradeID] AS [GradeID]
    FROM ( SELECT 
        [Limit1].[ID] AS [ID], 
        [Limit1].[EnrollmentDate] AS [EnrollmentDate], 
        [Limit1].[LastName] AS [LastName], 
        [Limit1].[FirstName] AS [FirstName], 
        [Limit1].[StudentID] AS [StudentID], 
        [Limit1].[Value] AS [Value], 
        [Extent3].[EnrollmentID] AS [EnrollmentID], 
        [Extent3].[CourseID] AS [CourseID], 
        [Extent3].[StudentID] AS [StudentID1], 
        [Extent3].[GradeID] AS [GradeID], 
        CASE WHEN ([Extent3].[EnrollmentID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM   (SELECT TOP (2) 
            [Extent1].[ID] AS [ID], 
            [Extent1].[EnrollmentDate] AS [EnrollmentDate], 
            [Extent1].[LastName] AS [LastName], 
            [Extent1].[FirstName] AS [FirstName], 
            [Extent2].[StudentID] AS [StudentID], 
            [Extent2].[Value] AS [Value]
            FROM  [dbo].[Student] AS [Extent1]
            LEFT OUTER JOIN [dbo].[GPA] AS [Extent2] ON [Extent1].[ID] = [Extent2].[StudentID]
            WHERE [Extent1].[ID] = @p__linq__0 ) AS [Limit1]
        LEFT OUTER JOIN [dbo].[Enrollment] AS [Extent3] ON [Limit1].[ID] = [Extent3].[StudentID]
    )  AS [Project2]
    ORDER BY [Project2].[StudentID] ASC, [Project2].[ID] ASC, [Project2].[C1] ASC: 
iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.NonQueryExecuted;Timespan:00:00:00.0012696;Properties:
Command: DELETE [dbo].[Enrollment]
WHERE ([EnrollmentID] = @0): 
iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.NonQueryExecuted;Timespan:00:00:00.0002634;Properties:
Command: DELETE [dbo].[Enrollment]
WHERE ([EnrollmentID] = @0): 
iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.NonQueryExecuted;Timespan:00:00:00.0002512;Properties:
Command: DELETE [dbo].[Enrollment]
WHERE ([EnrollmentID] = @0): 
iisexpress.exe Error: 0 : Error executing command: DELETE [dbo].[Student]
WHERE ([ID] = @0) Exception: System.Data.SqlClient.SqlException (0x80131904): The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.GPA_dbo.Student_StudentID". The conflict occurred in database "<databasename>", table "dbo.GPA", column 'StudentID'.
The statement has been terminated.

Câu trả lời:


7

Đó là một câu hỏi về thời gian. Cân nhắc xóa StudentID # 1:

  1. Hàng bị xóa khỏi Studentbảng
  2. Việc xóa tầng sẽ xóa các hàng tương ứng khỏi Enrollment
  3. Mối quan hệ khóa ngoại GPA-> Studentđược kiểm tra
  4. Kích hoạt kích hoạt, gọi MergeGPA

Tại thời điểm này, MergeGPAkiểm tra xem liệu có mục nào cho Sinh viên số 1 trong GPAbảng không. Không có (nếu không, kiểm tra FK ở bước 3 sẽ phát sinh lỗi).

Vì vậy, WHEN NOT MATCHEDmệnh đề trong các MergeGPAlần thử thành INSERTmột hàng trong GPAStudentID # 1. Nỗ lực này không thành công (với lỗi FK) vì StudentID # 1 đã bị xóa khỏi Studentbảng (ở bước 1).


1
Tôi nghĩ rằng bạn đang ở một cái gì đó. Khi một học sinh được tạo ra với các tuyển sinh, nhưng không có điểm nào được chỉ định, học sinh đó không có mục nào trong bảng GPA. Khi cơ sở dữ liệu xóa sinh viên đó, nó nhìn vào cơ sở dữ liệu, thấy đăng ký để xóa nhưng không có mục nhập GPA. Vì vậy, nó đặt ra về việc xóa các đăng ký, điều này gây ra một kích hoạt để tạo ra mục nhập GPA, sau đó gây ra vi phạm ràng buộc? Vì vậy, giải pháp là tạo một mục GPA khi tôi tạo một học sinh. Sau đó, trình kích hoạt chèn của tôi sẽ không cần điều kiện và quy trình được lưu trữ của tôi sẽ không cần phải là hợp nhất, chỉ là một bản cập nhật.
DowntownHippie

-1

Không cần đọc tất cả, chỉ từ sơ đồ: Bạn có một mục trong Ghi danh hoặc một mục trong GPA chỉ vào Học sinh bạn muốn xóa.

Các mục nhập có khóa ngoại trước tiên cần được xóa (hoặc các khóa được đặt thành null, nhưng đó là thông lệ xấu) trước khi bạn có thể xóa mục Sinh viên.

Ngoài ra, một số cơ sở dữ liệu đã BẬT XÓA CASCADE, sẽ xóa bất kỳ mục nào có khóa ngoại sang mục bạn muốn xóa.

Một cách khác là không khai báo chúng là khóa ngoại và chỉ sử dụng giá trị khóa, nhưng điều đó cũng không được đề xuất lại.


Trong trường hợp không thành công, có một mục trong Ghi danh nhưng không có mục nào trong GPA.
DowntownHippie

bạn có một số ràng buộc với ON DELETE CASCADE và một số không có. hãy thử thêm dòng đó vào tất cả các ràng buộc. sau đó sẽ thử vô hiệu hóa tất cả các kích hoạt và sau thử nghiệm đó với một thiết lập tối thiểu. chúc may mắn
user44286 23/07/14

Tôi thấy những ON DELETE CASCADEtuyên bố đó. Không có câu lệnh tạo bảng nào, cũng không phải câu lệnh xóa được viết bằng tay, tất cả chúng đều được tạo bởi thực thể. Các tầng là vì đăng ký có khóa ngoại không phải là khóa chính; Ràng buộc khóa ngoại của GPA là khóa chính vì vậy nó không cần một tầng. Tôi đã kiểm tra điều này, nếu bạn xóa một học sinh với mục nhập bảng GPA, mục đó sẽ bị xóa. Vấn đề duy nhất là một sinh viên có tuyển sinh nhưng không có gpa.
DowntownHippie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.