Jak ograniczyć maksymalną liczbę wierszy w tabeli do tylko 1

22

Mam tabelę konfiguracji w mojej bazie danych SQL Server i ta tabela powinna mieć tylko jeden wiersz. Aby pomóc przyszłym programistom zrozumieć to, chciałbym zapobiec dodawaniu więcej niż jednego wiersza danych. Zdecydowałem się użyć do tego wyzwalacza, jak poniżej ...

ALTER TRIGGER OnlyOneConfigRow
    ON [dbo].[Configuration]
    INSTEAD OF INSERT
AS
BEGIN
    DECLARE @HasZeroRows BIT;
    SELECT  @HasZeroRows = CASE
        WHEN COUNT (Id) = 0 THEN 1
        ELSE 0
    END
    FROM
        [dbo].[Configuration];

    IF EXISTS(SELECT [Id] FROM inserted) AND @HasZeroRows = 0
    BEGIN
        RAISERROR ('You should not add more than one row into the config table. ', 16, 1)    
    END
END

Nie powoduje to błędu, ale nie pozwala wejść do pierwszego wiersza.

Czy istnieje też bardziej skuteczny / bardziej samoobjaśniający sposób ograniczenia liczby wierszy, które można wstawić do tabeli do 1, niż ten? Czy brakuje mi wbudowanej funkcji SQL Server?

Dib
źródło
2
Jako wyjaśnienie, dlaczego twoje oryginalne podejście nie działało: używasz wyzwalacza Zamiast zamiast, co oznacza, że ​​kod jest uruchamiany zamiast instrukcji insert. Aby wstawka mogła się zdarzyć, musisz jawnie dołączyć ją jako część wyzwalacza.
Scott M

Odpowiedzi:

52

Te dwa ograniczenia byłyby wystarczające:

CREATE TABLE dbo.Configuration
( ConfigurationID TINYINT NOT NULL DEFAULT 1,
  -- the rest of the columns
  CONSTRAINT Configuration_PK 
    PRIMARY KEY (ConfigurationID),
  CONSTRAINT Configuration_OnlyOneRow 
    CHECK (ConfigurationID = 1)
) ;

Potrzebujesz zarówno PRIMARY KEY(lub UNIQUEograniczenia), aby żadne dwa wiersze nie miały tej samej IDwartości, a CHECKograniczenie, aby wszystkie wiersze miały tę samą IDwartość (dowolnie wybrane 1).
W połączeniu dwa prawie przeciwne ograniczenia ograniczają liczbę wierszy do zera lub jednego.


Na fikcyjnym DBMS (żadna bieżąca implementacja SQL nie pozwala na taką konstrukcję), który pozwala na klucz podstawowy składający się z 0 kolumn, byłoby to również rozwiązanie:

CREATE TABLE dbo.Configuration
( -- no ConfigurationID needed at all
  -- the rest of the columns
  CONSTRAINT Configuration_PK 
    PRIMARY KEY ()                -- 0 columns!
) ;
ypercubeᵀᴹ
źródło
24

Możesz zdefiniować identyfikator jako kolumnę obliczeniową, której wynikiem będzie stała wartość, i zadeklaruj tę kolumnę jako unikalną:

CREATE TABLE dbo.Configuration
(
  ID AS CAST(1 AS tinyint),  -- or: AS bit
  ...  -- other columns
  CONSTRAINT UQ_Configuration_ID UNIQUE (ID)
);
Andriy M.
źródło
9

Możesz także użyć wyzwalacza ..

create trigger LimitTable
on YourTableToLimit
after insert
as
    declare @tableCount int
    select @tableCount = Count(*)
    from YourTableToLimit

    if @tableCount > 50
    begin
        rollback
    end
go
topher
źródło
1

Wydaje się to trochę dziwnym wymaganiem, ale dziwne :) Możesz mieć ograniczenie na stole, a następnie zezwalać tylko na aktualizacje (bez wstawiania lub usuwania) w tabeli?

CREATE TABLE dbo.Config (
    ID INT identity(1,1), 
    CONFIGURATION VARCHAR(MAX),
    constraint ck_limitrows CHECK (ID <=1) 
    );

Jest to trochę hackey sposób, aby to zrobić, czy nie byłoby lepiej po prostu wymusić zmiany w konfiguracji za pomocą procedury składowanej, która może następnie obsłużyć całą tę logikę?

Mata
źródło
2
Tylko upewnij się, że nikt nie może usunąć z tabeli. Jeśli ktoś usunie, a następnie spróbuje ponownie wstawić, spróbuje wstawić o tożsamości 2, na co nie zezwoli.
Mat.
5
To nie zabrania IDposiadania wartości 0ujemnej. A jak wskazuje @Mat, nie powiedzie się, jeśli spróbujesz wstawić kolejny wiersz, jeśli pierwszy zostanie usunięty.
ypercubeᵀᴹ
2
Jeśli chodzi o „dziwne wymaganie”, wolę używać tabeli z jednym wierszem do ustawień konfiguracji, zamiast pozornie bardziej powszechnego projektu EAV . Zaletą tego pierwszego jest to, że kolumny można tworzyć z odpowiednim typem danych i można dodawać odpowiednie ograniczenia (łatwiej).
Kenny Evitt,
2
Być może mój poprzedni komentarz nie był zbyt jasny. Efektem ubocznym „To nie zabrania ID mieć wartości 0 lub ujemnej” jest to, że tabela może kończyć się 2 lub więcej wierszami. Właściwość tożsamości nie oznacza wyjątkowego ograniczenia.
ypercubeᵀᴹ
3
Aby zilustrować to, co powiedział @ ypercubeᵀᴹ, dzięki temu rozwiązaniu możesz zrobić np. INSERT INTO dbo.Config DEFAULT VALUES;Tylko raz, ale możesz śledzić to SET IDENTITY_INSERT dbo.Config ON; INSERT INTO dbo.Config (ID) VALUES (0); SET IDENTITY_INSERT dbo.Config OFF; wiele razy, a skończysz na tabeli z wieloma wierszami.
Andriy M,