Stuck bei der Schaffung eines Auslösers

einige MSSQL-Übungen durchzuführen, und ich versuche, einen Auslöser zu erstellen. Doch die Lösung, die ich habe, stößt mir als theoretisch korrekt an, aber es funktioniert nicht.

Ziel ist es, einen Auslöser für einen Tisch zu erstellen, der nur zwei Spalten hat. Eine Spalte ist der Primärschlüssel und ist Identität und erlaubt keine Nullwerte. Die andere Spalte ist eine, die NULL-Werte nennt. Allerdings erlaubt es NULL-Werte NUR FÜR EIN SINGLE ROW in der gesamten Tabelle. Grundsätzlich sollte ein Trigger für eine Insert / Update-Operation auf dieser Tabelle ausgetriggers werden, die versucht, die Spalte in einen NULL-Wert einzufügen / zu aktualisieren, wenn bereits ein vorhandener NULL-Wert für die Spalte in der Tabelle vorhanden ist.

Diese Bedingung erfasse ich in meinem Trigger-Code wie folgt:

After Insert, Update AS set ANSI_WARNINGS OFF If ( (select count(NoDupName) from TestUniqueNulls where NoDupName is null) > 1 ) BEGIN Print 'There already is a row that contains a NULL value, transaction aborted'; ROLLBACK TRAN END 

Die Transaktion läuft jedoch trotzdem aus. Ich bin mir nicht sicher, warum das passiert und der Auslöser schießt sich nicht.

Also irgendjemand, um meine Bedenken hier aufzuklären?

Ich habe auch set ANSI_WARNINGS OFF am Anfang des Triggers verwendet.

count(col) zählt nur null Nullwerte so count(NoDupName) ... where NoDupName is null ist, ist immer null. Du musst stattdessen die count(*) überprüfen.

Ich weiß, das ist nur eine Übung, aber eine indizierte view könnte ein besserer Mechanismus dafür sein.

 CREATE VIEW dbo.NoMoreThanOneNull WITH SCHEMABINDING AS SELECT NoDupName FROM dbo.TestUniqueNulls WHERE NoDupName IS NULL GO CREATE UNIQUE CLUSTERED INDEX ix ON dbo.NoMoreThanOneNull(NoDupName) 

Ja, das ist ein Gotcha. Der Ausdruck innerhalb parens des COUNT muss ausgewertet werden, um nicht zu null, sonst wird es nicht gezählt. So ist es sicherer zu verwenden * , oder 1 oder eine nicht nullable Spalte in den Ausdruck. Der am häufigsten angetriebene Ausdruck ist '*' , obwohl man auch über '1' kommen kann. Es gibt keinen Unterschied zwischen diesen Ausdrücken in Bezug auf performance. Allerdings, wenn Sie Ausdruck verwenden, der zu null (wie nullable Spalte) auswerten kann, können Ihre Zählungen und andere Aggregationen vollständig aus sein.

 create table nulltest(a int null) go insert nulltest(a) values (1), (null), (2) go select * from nulltest select COUNT(*) from nulltest select COUNT(1) from nulltest select COUNT(a) from nulltest go drop table nulltest