it-swarm.com.de

STANDARDBESCHRÄNKUNG, lohnt es sich?

Normalerweise entwerfe ich meine Datenbanken nach den folgenden Regeln:

  • Niemand anderes als db_owner und sysadmin haben Zugriff auf die Datenbanktabellen.
  • Benutzerrollen werden auf Anwendungsebene gesteuert. Normalerweise verwende ich eine Datenbankrolle, um Zugriff auf die Ansichten, gespeicherten Prozeduren und Funktionen zu gewähren. In einigen Fällen füge ich jedoch eine zweite Regel hinzu, um einige gespeicherte Prozeduren zu schützen.
  • Ich benutze TRIGGERS, um zunächst kritische Informationen zu validieren.
CREATE TRIGGER <TriggerName>
ON <MyTable>
[BEFORE | AFTER] INSERT
AS
    IF EXISTS (SELECT 1 
               FROM   inserted
               WHERE  Field1 <> <some_initial_value>
               OR     Field2 <> <other_initial_value>)
    BEGIN
        UPDATE MyTable
        SET    Field1 = <some_initial_value>,  
               Field2 = <other_initial_value>  
        ...  
    END
  • DML wird mit gespeicherten Prozeduren ausgeführt:
sp_MyTable_Insert(@Field1, @Field2, @Field3, ...);
sp_MyTable_Delete(@Key1, @Key2, ...);
sp_MyTable_Update(@Key1, @Key2, @Field3, ...);

Denken Sie, dass es sich in diesem Szenario lohnt, DEFAULT CONSTRAINTs zu verwenden, oder füge ich dem DB-Server einen zusätzlichen und unnötigen Job hinzu?

pdate

Ich verstehe, dass ich durch die Verwendung der DEFAULT-Einschränkung mehr Informationen an eine andere Person weitergebe, die die Datenbank verwalten muss. Aber ich interessiere mich hauptsächlich für Leistung.

Ich gehe davon aus, dass die Datenbank immer Standardwerte überprüft, auch wenn ich den richtigen Wert gebe, daher mache ich den gleichen Job zweimal.

Gibt es beispielsweise eine Möglichkeit, die DEFAULT-Einschränkung innerhalb einer Triggerausführung zu vermeiden?

20
McNets

Ich gehe davon aus, dass die Datenbank immer Standardwerte überprüft, auch wenn ich den richtigen Wert gebe, daher mache ich den gleichen Job zweimal.

Warum würden Sie das annehmen? ;-). Angesichts der Tatsache, dass Standardeinstellungen vorhanden sind, um einen Wert bereitzustellen, wenn die Spalte, an die sie angehängt sind, nicht in der Anweisung INSERT vorhanden ist, würde ich das genaue Gegenteil annehmen: Sie werden vollständig ignoriert, wenn die zugehörige Spalte in der Anweisung INSERT Anweisung.

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 erstellen, damit SQL Server (die wahre Autorität hier) diese Frage beantworten kann.

[~ # ~] setup [~ # ~]

Führen Sie Folgendes einmal 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 jedes Mal mehrmals aus, um ein Gefühl für das durchschnittliche Timing für jedes einzelne zu bekommen.

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 jedes Mal mehrmals aus, um ein Gefühl für das durchschnittliche Timing für jedes einzelne zu bekommen.

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 sehen, dass es keinen wirklichen zeitlichen Unterschied zwischen den Tests 1A und 1B oder zwischen den Tests 2A und 2B gibt. Nein, es gibt keine Leistungseinbußen, wenn ein DEFAULT definiert, aber nicht verwendet wird.

Neben der bloßen Dokumentation des beabsichtigten Verhaltens müssen Sie auch berücksichtigen, dass es hauptsächlich Sie sind, die sich darum kümmern, dass die DML-Anweisungen vollständig in Ihren gespeicherten Prozeduren enthalten sind. Support-Leute kümmern sich nicht darum. Zukünftige Entwickler sind sich möglicherweise Ihres Wunsches nicht bewusst, alle DMLs in diese gespeicherten Prozeduren zu kapseln, oder kümmern sich auch dann darum, wenn sie es wissen. Und wer auch immer diese Datenbank unterhält, nachdem Sie gegangen sind (entweder ein anderes Projekt oder ein anderer Job), kümmert sich möglicherweise nicht darum oder kann die Verwendung eines ORM nicht verhindern, egal wie sehr sie protestieren. Standardeinstellungen können also helfen, indem sie den Leuten ein "out" geben, wenn sie eine INSERT ausführen, insbesondere eine Ad-hoc INSERT, die von einem Supportmitarbeiter durchgeführt wird, da dies eine Spalte ist, die sie nicht benötigen einschließen (weshalb ich immer Standardeinstellungen für Prüfdatumsspalten verwende).


UND, mir ist gerade eingefallen, dass ziemlich objektiv angezeigt werden kann, ob ein DEFAULT überprüft wird, wenn die zugehörige Spalte in der Anweisung INSERT 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 können, wird der Standardwert zu 100% ignoriert, wenn eine Spalte (und ein Wert, nicht das Schlüsselwort DEFAULT) angegeben wird. Wir wissen das, weil INSERT erfolgreich ist. Wenn jedoch die Standardeinstellung verwendet wird, tritt beim endgültigen Ausführen ein Fehler auf.


Gibt es eine Möglichkeit, die DEFAULT-Einschränkung innerhalb einer Triggerausführung zu vermeiden?

Das Vermeiden von Standardbeschränkungen (zumindest in diesem Zusammenhang) ist zwar völlig unnötig, der Vollständigkeit halber kann jedoch angemerkt werden, dass es nur möglich wäre, eine Standardbeschränkung innerhalb eines INSTEAD OF - Triggers zu "vermeiden" nicht innerhalb eines AFTER -Triggers. Gemäß der Dokumentation für CREATE TRIGGER :

Wenn Einschränkungen für die 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.

Die Verwendung eines INSTEAD OF - Triggers würde natürlich Folgendes erfordern:

  1. Deaktivieren der Standardbeschränkung
  2. Erstellen eines AFTER -Triggers, der die Einschränkung aktiviert

Ich würde dies jedoch nicht unbedingt empfehlen.

22
Solomon Rutzky

Ich sehe keinen wesentlichen Schaden darin, Standardbeschränkungen zu haben. Tatsächlich sehe ich einen besonderen Vorteil: Sie haben den Standard auf derselben Logikstufe wie die Tabellendefinition selbst definiert. Wenn Sie einen Standardwert in Ihrer gespeicherten Prozedur haben, muss jemand dorthin gehen, um herauszufinden, wie hoch der Standardwert ist. und das ist nicht unbedingt für jemanden, der neu im System ist, offensichtlich (wenn Sie zum Beispiel morgen eine Milliarde Dollar erben, Ihre eigene tropische Insel kaufen und dort aufhören und umziehen, um einen anderen armen Saft zu hinterlassen, um Dinge herauszufinden auf eigene Faust).

9
RDFozz