Wie werden meine SQL Server-Einschränkungen umgangen?

8

Wir haben eine Handvoll Zeilen in unserer Datenbank gefunden, die gegen eine aktive Einschränkung verstoßen. Wie ist das möglich?

Die Einschränkung ist aktiv, da wir nicht einfach manuell eine Zeile hinzufügen können, die diese Einschränkung umgeht. Wenn wir jedoch laufen CHECKCONSTRAINTS(Files), stellen wir fest, dass es während unserer Testläufe gelegentlich umgangen wurde. Die fraglichen Reihen wurden alle innerhalb einer halben Sekunde voneinander erstellt, was auf eine Art Rennbedingung hindeutet.

Hier ist die Einschränkung, die auf die Tabelle angewendet wird. Die Regel soll die Eindeutigkeit des Namens in einem bestimmten übergeordneten Ordner sicherstellen:

ALTER TABLE Files ADD CONSTRAINT UniqueNameInParentFolder CHECK
    CheckUniqueNameInFolder(ParentFoldersID, Name) = 1;

Diese Einschränkung ruft eine Funktion auf, die folgendermaßen aussieht:

-- first check for the new name in the Folders table
IF ((SELECT COUNT(*) FROM Folders 
     WHERE ParentFoldersID = @FoldersID AND Name = @Name) = 0)
BEGIN 
    -- then check for it in the Files table
    IF ((SELECT COUNT(*) FROM Files 
         WHERE ParentFoldersID = @FoldersID AND Name = @Name) <= 1)
        RETURN 1
END
RETURN 0

Einzelne Zeilen werden innerhalb von Transaktionen hinzugefügt, daher fällt es mir schwer zu verstehen, wie sich doppelte Zeilen über diese Einschränkung hinausschleichen.

beladen
quelle
Ja, wir haben eine analoge Einschränkung, dass Folderses dort auch eine ähnliche Anzahl von Verstößen gibt. Wir verwenden derzeit READ_COMMITTED_SNAPSHOT.
Beladung

Antworten:

16

Überprüfungsbeschränkungen, die auf UDFs basieren, sind Müll. Parallelität, RBAR, Isolation usw., wie Sie herausgefunden haben. Einige Links:

Der sicherste Weg für SQL Server wäre in diesem Fall die Verwendung von Standardeinschränkungen wie eindeutigen und Fremdschlüsseln. Ich kann nicht verstehen, warum Sie die Ordnertabelle auf eine Einschränkung der Dateitabelle überprüfen

Edit: eine Datei und einen Ordner mit dem gleichen Namen in einem bestimmten übergeordneten Ordner verhindern nur , verwenden Sie eine indizierte Sicht. Doppelte Dateien oder doppelte Ordner erfordern die Eindeutigkeit auf Tabellenebene.

CREATE VIEW CheckUnique
WITH SCHEMABINDING
AS
SELECT fo.ParentFoldersID, fo.Name
FROM
   Folders fo
   JOIN
   File fi ON fo.ParentFoldersID  = fi.ParentFoldersID AND fo.Name = fi.Name
GO
CREATE UNIQUE CLUSTERED INDEX IXCU_CheckUnique ON CheckUnique (ParentFoldersID, Name)
GO

Oder ein Auslöser.

Aber niemals eine UDF in einer Prüfbedingung

gbn
quelle
In unserem Fall befinden sich Ordner und Dateien mit demselben Namen möglicherweise nicht im selben übergeordneten Ordner. Daher halte ich eindeutige / fk-Einschränkungen leider nicht für ausreichend.
Beladung
1
+1 Ein weiteres interessantes Beispiel von Tibor Karaszi: sqlblog.com/blogs/tibor_karaszi/archive/2009/12/17/… zeigt, wie Prüfbeschränkungen mit udfs falsch negativ sind.
AK
12

Diese CheckUniqueNameInFolder-Funktion überprüft kaum etwas. Unter dieser Einschränkungsprüfung können viele Duplikate hinzugefügt werden. Es gibt zwei verschiedene SELECTS, die nacheinander ausgeführt werden (daher kann die von der ersten Auswahl überprüfte Bedingung zum Zeitpunkt der Ausführung der zweiten Auswahl ungültig werden), und in jedem Fall gibt die Einschränkung bestenfalls an, dass bei der Prüfung kein Duplikat vorhanden war aufgetreten ist, bedeutet in keiner Weise, dass beim Einfügen / Aktualisieren kein Duplikat vorhanden ist . Da die Überprüfungen die zu überprüfenden Schlüssel im U- oder X-Modus nicht sperren , können mehrere Einfügungen gleichzeitig erfolgen. Führen Sie die Überprüfung durch, suchen Sie kein Duplikat und alle fügen denselben Eintrag ein.

Die einzige Möglichkeit, eine eindeutige Einschränkung ordnungsgemäß durchzusetzen, besteht in der Verwendung einer eindeutigen Einschränkung .

Erstellen Sie eine berechnete Spalte mit dem vollständigen Pfad Ihrer 'Dateien' und erzwingen Sie die Eindeutigkeit des gesamten Pfads mit einer EINZIGARTIGEN Einschränkung oder verwenden Sie möglicherweise eine EINZIGARTIGE Einschränkung für (ParentFolderID, Name). Speichern Sie Ordner und Dateien nicht separat, sondern verwenden Sie eine gemeinsame Tabelle für Ordner und Dateien (z. B. Einträge), da sie denselben Namespace belegen .

Remus Rusanu
quelle
1
Ja, aus irgendeinem Grund haben wir angenommen, dass die Transaktion die Probleme behandeln würde, die wir sehen, aber Sie haben Recht, dass es keine Sperre gibt. (Das Zusammenführen der Tabellen ist zu diesem Zeitpunkt eine ziemlich große Neugestaltung für uns, aber dennoch eine interessante Idee.)
beladen
4

Das Ausführen innerhalb einer Transaktion auf Standardebene read committedschlägt unter Last fehl.

Die Lesevorgänge schließen sich nicht gegenseitig aus und werden serialisiert, sodass zwei gleichzeitige Transaktionen beide lesen können, dass die Zeile nicht vorhanden ist. Sie können UPDLOCK,ROWLOCK,HOLDLOCKdem SELECT.

Martin Smith
quelle