masalah pelanggaran batasan kunci asing

10

Saya telah mengidentifikasi 3 situasi.

  1. Seorang siswa tanpa pendaftaran.
  2. Seorang siswa dengan pendaftaran tetapi tidak memiliki nilai.
  3. Seorang siswa dengan pendaftaran dan nilai.

Ada pemicu pada tabel pendaftaran untuk menghitung IPK. Jika seorang siswa memiliki nilai, itu akan memperbarui atau memasukkan entri ke dalam tabel IPK; tidak ada nilai, tidak ada entri tabel IPK.

Saya dapat menghapus siswa tanpa pendaftaran (# 1). Saya dapat menghapus seorang siswa dengan pendaftaran dan nilai (# 3 di atas). Tetapi saya tidak dapat menghapus seorang siswa dengan pendaftaran tetapi tidak ada nilai (# 2). Saya mendapatkan pelanggaran batasan referensi.

Pernyataan DELETE bertentangan dengan batasan REFERENSI "FK_dbo.GPA_dbo.Student_StudentID". Konflik terjadi di basis data "", tabel "dbo.GPA", kolom 'StudentID'.

Jika saya tidak dapat menghapus siswa baru tanpa pendaftaran (dan tidak ada entri IPK) maka saya akan memahami pelanggaran kendala, tetapi saya dapat menghapus siswa itu. Ini adalah siswa dengan pendaftaran dan tanpa nilai (dan masih tidak ada IPK) yang tidak dapat saya hapus.

Saya telah memperbaiki pemicu saya sehingga saya bisa maju. Sekarang, jika Anda memiliki pendaftaran, pemicu memasukkan Anda ke dalam tabel IPK, apa pun yang terjadi. Tapi saya tidak mengerti masalah yang mendasarinya. Penjelasan apa pun akan sangat dihargai.

Untuk apa nilainya:

  1. Visual Studio 2013 Professional.
  2. IIS express (internal ke VS2013).
  3. Aplikasi Web ASP.NET menggunakan EntityFramework 6.1.1.
  4. MS SQL Server 2014 Enterprise.
  5. GPA.Value tidak dapat dibatalkan.
  6. Enrollment.GradeID tidak dapat dibatalkan.

Berikut ini cuplikan dari basis data:

gambar basis data

- EDIT -

Semua tabel dibuat oleh EntityFramework, saya menggunakan SQL Server Management Studio untuk menghasilkan ini.

Berikut adalah tabel buat pernyataan dengan kendala .:

GPA meja:

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

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

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]

Inilah pemicunya :

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

Tambalan untuk bergerak maju adalah mengomentari garis-garis di AFTER INSERTpelatuk itu.

Berikut adalah prosedur tersimpan :

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)));

Berikut adalah fungsi basis datanya :

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

Berikut ini adalah hasil debug dari metode hapus pengontrol, pernyataan pilih adalah metode yang menanyakan apa yang akan dihapus. Siswa ini memiliki 3 pendaftaran, REFERENCEmasalah kendala terjadi ketika pendaftaran ke-3 dihapus. Saya kira EF menggunakan transaksi karena pendaftaran tidak dihapus.

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.
DowntownHippie
sumber

Jawaban:

7

Ini masalah waktu. Pertimbangkan menghapus StudentID # 1:

  1. Baris dihapus dari Studenttabel
  2. Penghapusan kaskade menghapus baris yang sesuai dari Enrollment
  3. Hubungan kunci asing GPA-> Studentdicentang
  4. Pemicu kebakaran, memanggil MergeGPA

Pada titik ini, MergeGPAperiksa untuk melihat apakah ada entri untuk Siswa # 1 di GPAtabel. Tidak ada (jika tidak, pemeriksaan FK pada langkah 3 akan menimbulkan kesalahan).

Jadi, WHEN NOT MATCHEDklausa dalam MergeGPAupaya untuk INSERTbaris dalam GPAuntuk StudentID # 1. Upaya ini gagal (dengan kesalahan FK) karena StudentID # 1 telah dihapus dari Studenttabel (pada langkah 1).

Paul White 9
sumber
1
Saya pikir Anda sedang melakukan sesuatu. Ketika seorang siswa dibuat dengan pendaftaran, tetapi tidak ada nilai yang ditugaskan, siswa itu tidak memiliki entri dalam tabel IPK. Ketika database menghapus siswa yang terlihat di database, melihat pendaftaran untuk dihapus tetapi tidak ada entri IPK. Jadi itu mengatur tentang menghapus pendaftaran, yang menyebabkan pemicu untuk memicu yang menciptakan entri IPK, yang kemudian menyebabkan pelanggaran kendala? Jadi solusinya adalah membuat entri IPK ketika saya membuat siswa. Maka pemicu sisipan saya tidak akan membutuhkan kondisi, dan prosedur tersimpan saya tidak perlu digabung, hanya pembaruan.
DowntownHippie
-1

Tanpa membaca semua, hanya dari diagram: Anda memiliki entri di Pendaftaran atau satu di IPK yang menunjuk ke Siswa yang ingin Anda hapus.

Entri dengan kunci asing harus dihapus terlebih dahulu (atau kunci diatur ke nol, tapi itu praktik buruk) sebelum Anda dapat menghapus entri Siswa.

Juga beberapa database memiliki ON DELETE CASCADE, yang akan menghapus semua entri dengan kunci asing ke yang ingin Anda hapus.

Cara lain adalah dengan tidak mendeklarasikannya sebagai kunci asing dan hanya menggunakan nilai kunci, tetapi itu juga tidak direkomendasikan.

pengguna44286
sumber
Dalam kasus di mana gagal, ada entri di Pendaftaran tetapi tidak ada di IPK.
DowntownHippie
Anda memiliki beberapa kendala dengan ON DELETE CASCADE dan beberapa tanpa. coba tambahkan baris itu ke semua kendala. setelah itu akan coba nonaktifkan semua trigger dan setelah itu tes dengan setup minimal. semoga sukses
user44286
Saya melihat ON DELETE CASCADEpernyataan itu. Tidak satu pun dari pernyataan pembuatan tabel ini, maupun pernyataan penghapusan yang ditulis tangan, semuanya dihasilkan oleh entitasframework. Kaskade adalah karena pendaftaran memiliki kunci asing yang bukan kunci utama; Batasan kunci asing IPK adalah kunci primer sehingga tidak perlu kaskade. Saya sudah menguji ini, jika Anda menghapus seorang siswa dengan entri tabel IPK, entri itu akan dihapus. Satu-satunya masalah adalah siswa dengan pendaftaran tetapi tidak ada gpa.
DowntownHippie