Ich habe 3 Situationen identifiziert.
- Ein Student ohne Einschreibungen.
- Ein Student mit Einschreibungen, aber ohne Noten.
- Ein Student mit Einschreibungen und Noten.
In der Registrierungstabelle befindet sich ein Auslöser für die Berechnung des GPA. Wenn ein Schüler Noten hat, wird er aktualisiert oder einen Eintrag in die GPA-Tabelle einfügen. Keine Noten, kein GPA-Tabelleneintrag.
Ich kann einen Studenten ohne Einschreibung löschen (# 1). Ich kann einen Schüler mit Einschreibungen und Noten löschen (Nr. 3 oben). Aber ich kann keinen Studenten mit Einschreibungen, aber ohne Noten löschen (# 2). Ich erhalte eine Verletzung der Referenzbedingung.
Die DELETE-Anweisung stand in Konflikt mit der REFERENCE-Einschränkung "FK_dbo.GPA_dbo.Student_StudentID". Der Konflikt trat in der Datenbank "", Tabelle "dbo.GPA", Spalte 'StudentID' auf.
Wenn ich einen neuen Schüler ohne Einschreibungen (und ohne GPA-Eintrag) nicht löschen könnte, würde ich die Verletzung der Einschränkung verstehen, aber ich kann diesen Schüler löschen. Es ist ein Student mit Einschreibungen und ohne Noten (und immer noch ohne GPA-Eintrag), den ich nicht löschen kann.
Ich habe meinen Abzug gepatcht, damit ich vorwärts gehen kann. Wenn Sie jetzt Registrierungen haben, fügt der Trigger Sie in die GPA-Tabelle ein, egal was passiert. Aber ich verstehe das zugrunde liegende Problem nicht. Jede Erklärung wäre sehr dankbar.
Für das, was es wert ist:
- Visual Studio 2013 Professional.
- IIS Express (intern für VS2013).
- ASP.NET Web App mit EntityFramework 6.1.1.
- MS SQL Server 2014 Enterprise.
- GPA.Value ist nullbar.
- Enrollment.GradeID ist nullwertfähig.
Hier ist ein Ausschnitt aus der Datenbank:
- BEARBEITEN -
Die Tabellen werden alle von EntityFramework erstellt. Ich habe SQL Server Management Studio verwendet, um diese zu erstellen.
Hier sind die Anweisungen zum Erstellen von Tabellen mit Einschränkungen:
GPA
Tabelle:
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
Tabelle:
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
Tabelle:
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]
Hier sind die Auslöser :
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
Der Patch, um vorwärts zu kommen, bestand darin, diese Zeilen im AFTER INSERT
Trigger auskommentieren.
Hier ist die gespeicherte Prozedur :
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)));
Hier ist die Datenbank - Funktion :
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
Hier ist die Debug-Ausgabe der Löschmethode des Controllers. Die select-Anweisung ist die Methode, die abfragt, was gelöscht werden soll. Dieser Schüler hat 3 Einschreibungen. Das REFERENCE
Problem mit den Einschränkungen tritt auf, wenn die 3. Einschreibung gelöscht wird. Ich gehe davon aus, dass EF eine Transaktion verwendet, da die Registrierungen nicht gelöscht werden.
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.
quelle
Ohne alles zu lesen, nur aus dem Diagramm: Sie haben entweder einen Eintrag in der Registrierung oder einen in GPA, der auf den Schüler verweist, den Sie löschen möchten.
Die Einträge mit den Fremdschlüsseln müssen zuerst gelöscht werden (oder die Schlüssel werden auf null gesetzt, aber das ist eine schlechte Praxis), bevor Sie den Schülereintrag löschen können.
Einige Datenbanken verfügen auch über ON DELETE CASCADE, wodurch alle Einträge mit Fremdschlüsseln zu dem Eintrag gelöscht werden, den Sie löschen möchten.
Eine andere Möglichkeit besteht darin, sie nicht als Fremdschlüssel zu deklarieren und nur den Schlüsselwert zu verwenden. Dies wird jedoch auch nicht empfohlen.
quelle
ON DELETE CASCADE
Aussagen. Weder diese Tabellenerstellungsanweisungen noch die Löschanweisungen sind handgeschrieben, sie werden alle vom entityframework generiert. Die Kaskaden sind darauf zurückzuführen, dass die Registrierung Fremdschlüssel enthält, die nicht der Primärschlüssel sind. Die Fremdschlüsseleinschränkung von GPA ist der Primärschlüssel, daher sollte keine Kaskade erforderlich sein. Ich habe dies getestet. Wenn Sie einen Schüler mit einem GPA-Tabelleneintrag löschen, wird der Eintrag gelöscht. Das einzige Problem ist ein Student mit Einschreibungen, aber ohne gpa.