Ich gehe davon aus, dass die Datenbank immer Standardwerte überprüft, auch wenn ich den korrekten Wert eingebe. Daher mache ich den gleichen Job zweimal.
Warum würden Sie das annehmen? ;-). Da Standardwerte vorhanden sind, um einen Wert bereitzustellen, wenn die Spalte, an die sie angehängt sind, nicht in der INSERT
Anweisung vorhanden ist, würde ich das genaue Gegenteil annehmen: Sie werden vollständig ignoriert, wenn die zugehörige Spalte in der INSERT
Anweisung vorhanden ist.
Glücklicherweise muss keiner von uns aufgrund dieser Aussage in der Frage etwas annehmen:
Ich interessiere mich hauptsächlich für Leistung.
Fragen zur Leistung sind fast immer überprüfbar. Wir müssen also nur einen Test ausarbeiten, damit SQL Server (die wahre Autorität hier) diese Frage beantworten kann.
INSTALLIEREN
Führen Sie einmal Folgendes aus:
SET NOCOUNT ON;
-- DROP TABLE #HasDefault;
CREATE TABLE #HasDefault
(
[HasDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
[SomeInt] INT NULL,
[SomeDate] DATETIME NOT NULL DEFAULT (GETDATE())
);
-- DROP TABLE #NoDefault;
CREATE TABLE #NoDefault
(
[NoDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
[SomeInt] INT NULL,
[SomeDate] DATETIME NOT NULL
);
-- make sure that data file and Tran Log file are grown, if need be, ahead of time:
INSERT INTO #HasDefault ([SomeInt])
SELECT TOP (2000000) NULL
FROM [master].sys.[all_columns] ac1
CROSS JOIN [master].sys.[all_columns] ac2;
Führen Sie die Tests 1A und 1B einzeln aus, nicht zusammen, da dies das Timing verzerrt. Führen Sie die einzelnen Schritte mehrmals aus, um einen Überblick über das durchschnittliche Timing der einzelnen Schritte zu erhalten.
Test 1A
TRUNCATE TABLE #HasDefault;
GO
PRINT '#HasDefault:';
SET STATISTICS TIME ON;
INSERT INTO #HasDefault ([SomeDate])
SELECT TOP (1000000) '2017-05-15 10:11:12.000'
FROM [master].sys.[all_columns] ac1
CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO
Test 1B
TRUNCATE TABLE #NoDefault;
GO
PRINT '#NoDefault:';
SET STATISTICS TIME ON;
INSERT INTO #NoDefault ([SomeDate])
SELECT TOP (1000000) '2017-05-15 10:11:12.000'
FROM [master].sys.[all_columns] ac1
CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO
Führen Sie die Tests 2A und 2B einzeln aus, nicht zusammen, da dies das Timing verzerrt. Führen Sie die einzelnen Schritte mehrmals aus, um einen Überblick über das durchschnittliche Timing der einzelnen Schritte zu erhalten.
Test 2A
TRUNCATE TABLE #HasDefault;
GO
DECLARE @Counter INT = 0,
@StartTime DATETIME,
@EndTime DATETIME;
BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
INSERT INTO #HasDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);
Test 2B
TRUNCATE TABLE #NoDefault;
GO
DECLARE @Counter INT = 0,
@StartTime DATETIME,
@EndTime DATETIME;
BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
INSERT INTO #NoDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);
Sie sollten feststellen, dass zwischen den Tests 1A und 1B oder zwischen den Tests 2A und 2B kein wirklicher Zeitunterschied besteht. Also, nein, es gibt keine Leistungseinbußen, die DEFAULT
definiert, aber nicht verwendet werden.
Neben der bloßen Dokumentation des beabsichtigten Verhaltens müssen Sie auch berücksichtigen, dass es hauptsächlich Sie selbst ist, der sich darum kümmert, dass die DML-Anweisungen vollständig in Ihren gespeicherten Prozeduren enthalten sind. Support-Leute interessieren sich nicht. Zukünftige Entwickler sind sich möglicherweise nicht bewusst, dass Sie den Wunsch haben, alle DML-Dateien in diese gespeicherten Prozeduren einzuschließen, oder kümmern sich selbst dann darum, wenn sie es wissen. Und wer diese Datenbank verwaltet, nachdem Sie gegangen sind (entweder ein anderes Projekt oder eine andere Stelle), ist möglicherweise egal oder kann die Verwendung eines ORM nicht verhindern, egal wie sehr sie protestieren. Defaults können also dabei helfen, dass die Leute ein "out" bekommen, wenn sie eine INSERT
, insbesondere eine Ad-hoc- INSERT
Aktion eines Support-Mitarbeiters durchführen, da dies eine Spalte ist, die sie nicht einschließen müssen (weshalb ich beim Audit immer Defaults verwende Datumsspalten).
UND, mir ist gerade eingefallen, dass ziemlich objektiv gezeigt werden kann, ob ein DEFAULT
Häkchen gesetzt ist oder nicht , wenn die zugehörige Spalte in der INSERT
Anweisung vorhanden ist: Geben Sie einfach einen ungültigen Wert an. Der folgende Test macht genau das:
-- DROP TABLE #BadDefault;
CREATE TABLE #BadDefault
(
[BadDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
[SomeInt] INT NOT NULL DEFAULT (1 / 0)
);
INSERT INTO #BadDefault ([SomeInt]) VALUES (1234); -- Success!!!
SELECT * FROM #BadDefault; -- just to be sure ;-)
INSERT INTO #BadDefault ([SomeInt]) VALUES (DEFAULT); -- Error:
/*
Msg 8134, Level 16, State 1, Line xxxxx
Divide by zero error encountered.
The statement has been terminated.
*/
SELECT * FROM #BadDefault; -- just to be sure ;-)
GO
Wie Sie sehen, DEFAULT
wird der Standardwert zu 100% ignoriert , wenn eine Spalte (und kein Schlüsselwort ) angegeben wird. Wir wissen das, weil das INSERT
gelingt. Wenn jedoch die Standardeinstellung verwendet wird, liegt ein Fehler vor, während diese endgültig ausgeführt wird.
Gibt es eine Möglichkeit, die Einschränkung DEFAULT innerhalb einer Triggerausführung zu vermeiden?
Während das Vermeiden von Standardeinschränkungen (zumindest in diesem Zusammenhang) völlig unnötig ist, kann der Vollständigkeit halber angemerkt werden, dass es nur möglich wäre, eine Standardeinschränkung innerhalb eines INSTEAD OF
Auslösers, jedoch nicht innerhalb eines AFTER
Auslösers, zu "vermeiden" . In der Dokumentation zu CREATE TRIGGER heißt es :
Wenn Einschränkungen in der Triggertabelle vorhanden sind, werden diese nach der Ausführung des INSTEAD OF-Triggers und vor der Ausführung des AFTER-Triggers überprüft. Wenn die Einschränkungen verletzt werden, werden die INSTEAD OF-Triggeraktionen zurückgesetzt und der AFTER-Trigger wird nicht ausgelöst.
Natürlich INSTEAD OF
würde die Verwendung eines Triggers Folgendes erfordern:
- Deaktivieren der Standardeinschränkung
- Erstellen eines
AFTER
Triggers, der die Einschränkung aktiviert
Dies würde ich jedoch nicht unbedingt empfehlen.