Einschränkungen basierend auf anderen Spalten

7

Ist es möglich zu begrenzen, welche Werte in einer Spalte basierend auf anderen Werten in der Zeile zulässig sind?

Zum Beispiel meine Tabelle:

ID  Test_mode  Active
--  ---------  ------
1   1          Null
2   0          1
3   1          0

Gibt es eine Möglichkeit, den Wert von Test_modein zu ändern , 0wenn a 1in eingefügt wird?Active

ODER

Wenn Test_mode1 nicht das Einfügen / Aktualisieren von erlaubt istActive

ODER

Wirf einen Fehler aus, wenn Test_mode1 ist und ein Einfügen / Aktualisieren von Activeversucht wird.

Activekann nur NULL, 1, 0 UND nur 1 mit Test_modeals 0 sein.

Ich hoffe, das macht Sinn, wenn nicht, lass es mich wissen und ich werde die Frage aktualisieren.

Erzengel33
quelle
Ich wollte nur erklären, warum ich meine Antwort akzeptiert habe. Die Antworten von Jon und Kin waren äußerst hilfreich und informativ. Verwandte beantworten Links zu einer anderen Seite, die eine sehr gute Erklärung der Einschränkungen enthält.
Erzengel33

Antworten:

8

Zunächst einmal Willkommen bei dba.stackexchange.com und vielen Dank für Ihren Beitrag!

Ist es möglich zu begrenzen, welche Werte in einer Spalte basierend auf anderen Werten in der Zeile zulässig sind?

Ja, mit CHECK CONSTRAINTS wie hier beschrieben

Beispiel:

create table myTable (ID int identity(1,1)
                        , Test_mode int
                        , Active int 
                        )
go

-- Active can only be NULL, 1, 0, AND only 1 with Test_mode as 0.
ALTER TABLE myTable WITH CHECK ADD 
   CONSTRAINT ck_active CHECK (active IS NULL OR active IN (1, 0)) 
   go

-- some test data
insert into myTable (test_mode, Active) values (1, null)
insert into myTable (test_mode, Active) values (0, null)
insert into myTable (test_mode, Active) values (1, 0)
insert into myTable (test_mode, Active) values (0, 1)
insert into myTable (test_mode, Active) values (1, 1)

select * from myTable

-- Is there a way to either change the value of Test_mode to 0 if a 1 is inserted into Active

update myTable
set Test_mode = case when Active = 1 then  0
        else Test_mode 
        end
where Active = 1

Wenn Test_mode1 nicht das Einfügen / Aktualisieren von Active --OR-- zulässt, wird ein Fehler ausgegeben, wenn Test_mode 1 ist und versucht wird, Active einzufügen / zu aktualisieren.

Verwenden Sie TRY / CATCH wie hier beschrieben

Kin Shah
quelle
Danke für Ihre Antwort. Ich hatte keine Ahnung von Check Constraints. Die Links waren sehr hilfreich. Das Update müsste jedoch jedes Mal explizit definiert werden, wenn ich einen Wert einfügen möchte? Ich muss das so einrichten , dass , wenn einer unserer Entwickler eine gespeicherte Prozedur schreibt werden sie nicht haben (obwohl sie sollten!) Zu wissen , dass dies der Weg ist , um die Tabelle zu aktualisieren. Sie sollten nur in der Lage sein Update myTable Set Active = 1und test_modeauch aktualisiert werden. Wenn dies jedoch der einzige Weg ist, dann soll es so sein! Vielen Dank :)
Archangel33
@ Archangel33 Ja .. das ist die beste Wahl, um ein Update durchzuführen. Sie können das in den SP einwickeln, damit Sie es nicht jedes Mal tun müssen. Überprüfen Sie auch die TRY / CATCH-Methode auf Fehlergenerierung und Rückmeldung an den Benutzer. Wenn Sie der Meinung sind, dass ich Ihre Frage beantwortet habe, markieren Sie sie bitte als Antwort.
Kin Shah
1
Active int [...] CHECK (active IS NULL OR active IN (1, 0))- Dies scheint ein sinnlos schlechtes Design zu sein, da es bedeutet: "Bitte emulieren Sie einen Booleschen bitWert, aber speichern Sie ihn in einem weniger effizienten Typ. Ich muss mich daran erinnern, dass ich seine zulässigen Werte manuell einschränken muss."
underscore_d
3

Die erste Verteidigungslinie, die verhindert, dass ungültige Daten in Ihre Tabellen gelangen, sind die Datentypen der Spalten.

Wenn ein Prozess versucht, eine Spalte auf einen Wert einzufügen oder zu aktualisieren, der außerhalb des Bereichs des Datentyps liegt (oder NULLwenn die Spalte NULLs nicht zulässt ), schlägt der Vorgang sofort fehl, ohne dass Sie zusätzliche Arbeit leisten müssen.

Die Auswahl des Datentyps ist einer der wichtigsten Aspekte des Tabellendesigns.

Da Sie kein Schema veröffentlicht haben, werde ich eines basierend auf den von Ihnen angegebenen Informationen erstellen:

CREATE TABLE [dbo].[Tests]
(
    ID int IDENTITY(1, 1) PRIMARY KEY,
    Test_mode bit NOT NULL, /* Based on only seeing 0/1. Maybe tinyint? */
    Active bit NULL
);

Basierend auf diesem Design sind die verfügbaren Kombinationen bereits auf Folgendes beschränkt:

Testmodus aktiv
        0 NULL
        0 0
        0 1
        1 NULL
        1 0
        1 1

Alles andere führt dazu, dass ein Fehler ausgelöst wird. (Was gut ist.)


Gibt es eine Möglichkeit, den Wert von Test_mode auf 0 zu ändern, wenn eine 1 in Active eingefügt wird?

ODER

Wenn Test_mode 1 ist, darf das Einfügen / Aktualisieren von Active nicht zugelassen werden

ODER

Wirf einen Fehler aus, wenn Test_mode 1 ist und versucht wird, Active einzufügen / zu aktualisieren.

Aktiv kann nur NULL, 1, 0 UND nur 1 mit Test_mode als 0 sein.

Sie haben 4 verschiedene Möglichkeiten angegeben, um zu der zulässigen Wertekombination zu gelangen ( na ja , irgendwie). Dies sind sehr unterschiedliche Strategien mit sehr unterschiedlichen Implementierungsverhalten.

Ich bevorzuge die Verwendung von sogenannten deklarativen Einschränkungen. Mit anderen Worten, das Tabellenschema und die zugehörigen Objekte begrenzen die zulässigen Werte, indem explizit angegeben wird , was zulässig ist (oder manchmal, was nicht zulässig ist). Tatsächlich sind die Spaltendatentypen selbst eine Art deklarative Einschränkung. Je näher die Werte an den Tabellendaten liegen, desto einfacher und zuverlässiger können sie eingeschränkt werden. (Im Gegensatz dazu würde eine nicht deklarative oder aktive Einschränkung implementiert, indem ein Teil von T-SQL geschrieben wird, normalerweise entweder ein Tabellenauslöser oder ein Teil einer gespeicherten Prozedur.)

Die ersten drei Optionen können nur mit nicht deklarativen Mitteln implementiert werden. Der letzte ist jedoch deklarativ, also konzentrieren wir uns darauf:

Activekann nur NULL, 1, 0 UND nur 1 mit Test_modeals 0 sein.

Dies definiert, was Sie tatsächlich wollen, dh die zulässigen Wertekombinationen in der Tabelle. Beachten Sie, dass die gültigen Kombinationen nur von Spaltenwerten in derselben Zeile abhängen . Dies ist wichtig, da hiermit festgelegt wird, mit welchen Mechanismen die Einschränkung implementiert werden kann.

In diesem Fall können wir eine CHECKEinschränkung verwenden , bei der es sich um einen True / False-Test handelt, der anhand der Spaltenwerte 1 einer Zeile feststellt, ob eine Zeile gültig oder ungültig ist . Wenn der Test fehlschlägt, schlägt der Vorgang, bei dem versucht wurde, die Zeile zu ändern, mit einem Fehler fehl.

ALTER TABLE [dbo].[Tests] WITH CHECK
    ADD CONSTRAINT CC_Tests_TestMode_Active
        CHECK ((Test_mode != 0) OR ((Active IS NOT NULL) AND (Active = 1)));

Sie werden feststellen, dass ich das Prädikat so konstruiert habe, dass es auch dann weiter funktioniert, wenn Test_modees sich tatsächlich um einen (nicht nullbaren) Ganzzahltyp handelt. Das IS NOT NULLTeil ist erforderlich, da CHECKEinschränkungen Zeilen zulassen, in denen das Prädikat ausgewertet wird undefined.

1 Sie können verwendet werden, um außerhalb der aktuellen Zeile zu prüfen, aber dies ist eine schlechte Praxis, und darauf werde ich hier nicht näher eingehen. Verwenden Sie stattdessen einen Auslöser.

Jon Seigel
quelle
Entschuldigung für das Schema Ich werde daran denken, diese in zukünftigen Fragen zu posten. Die Verwendung von a Check Constraintscheint die am besten geeignete Lösung zu sein. Werden Einschränkungen nach einer Update-Anweisung überprüft? Was ich meine ist, wird ein UPDATELike UPDATE [dbo].[Tests] SET Active=1,test_mode=0 WHERE test_mode=1eine Einschränkung nicht bestehen oder wird der Status der Zeile erst nach UPDATEAbschluss überprüft . Ich werde einige Tests durchführen, um sicherzugehen, aber ich habe das Gefühl, dass dies funktionieren wird.
Erzengel33
@ Archangel33: Überprüfen Sie, ob Einschränkungen nur den endgültigen Status der Zeile überprüfen. Sie überprüfen nicht, ob Sie von einem Status in einen anderen wechseln dürfen oder nicht. Die von UPDATEIhnen erwähnte Aussage wird erfolgreich sein, da der Endzustand zulässig ist. Wenn Sie diese Statusänderung nicht zulassen möchten, müssen Sie dazu einen Trigger verwenden. (Ihre anfänglichen Anforderungen waren etwas unklar.) Hinweis: Ich würde empfehlen, sowohl eine Prüfbedingung als auch einen Auslöser zu verwenden, wenn dies Ihre Anforderungen sind.
Jon Seigel
Vielen Dank. Ja, ich werde meine Anforderungen beim nächsten Mal besser definieren. Nein, das ist perfekt. Ich bin froh zu hören, dass UPDATEdas funktionieren wird. Ich dachte, dass es beim Übergang vom aktuellen in den gewünschten Zustand zu einem ungültigen Zustand kommen könnte.
Erzengel33
2
CREATE UNIQUE INDEX [UNQ_IndexName]
  ON [dbo].[Table]([Column])
  WHERE   ([Status] in( 'A','D' ) );
Mohamed.Abdo
quelle
1
Dies stimmt zwar nicht mit dem vom OP dargestellten Beispiel überein, stellt jedoch tatsächlich ein Beispiel für Werte in einer Spalte dar, die die Werte in einer anderen Spalte begrenzen. Wenn Statusalles andere als Aoder ist D, dann Columnmüsste es nicht eindeutig sein.
RDFozz