Comment limiter le nombre maximal de lignes dans une table à seulement 1

22

J'ai une table de configuration dans ma base de données SQL Server et cette table ne doit avoir qu'une seule ligne. Pour aider les futurs développeurs à comprendre cela, j'aimerais empêcher l'ajout de plusieurs lignes de données. J'ai choisi d'utiliser un déclencheur pour cela, comme ci-dessous ...

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

Cela ne génère pas d'erreur mais ne permet pas à la première ligne d'entrer.

Existe-t-il également un moyen plus efficace / plus explicite de limiter le nombre de lignes pouvant être insérées dans une table à seulement 1? Suis-je en train de manquer une fonctionnalité SQL Server intégrée?

Dib
la source
2
Juste pour expliquer pourquoi votre approche d'origine ne fonctionnait pas: vous utilisez un déclencheur Au lieu de, ce qui signifie que votre code est exécuté à la place de l'instruction d'insertion. Donc, pour que l'insertion se produise, vous devez l'inclure explicitement dans le déclencheur.
Scott M

Réponses:

52

Ces deux contraintes feraient:

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)
) ;

Vous avez besoin à la fois de la PRIMARY KEY(ou d'une UNIQUEcontrainte) pour que deux lignes n'aient pas la même IDvaleur et de la CHECKcontrainte pour que toutes les lignes aient la même IDvaleur (choisie arbitrairement 1).
En combinaison, les deux contraintes presque opposées limitent le nombre de lignes à zéro ou à un.


Sur un SGBD fictif (aucune implémentation SQL actuelle ne permet cette construction) qui autorise une clé primaire composée de 0 colonnes, ce serait aussi une solution:

CREATE TABLE dbo.Configuration
( -- no ConfigurationID needed at all
  -- the rest of the columns
  CONSTRAINT Configuration_PK 
    PRIMARY KEY ()                -- 0 columns!
) ;
ypercubeᵀᴹ
la source
24

Vous pouvez définir l'ID comme une colonne calculée évaluant une valeur constante et déclarer cette colonne comme unique:

CREATE TABLE dbo.Configuration
(
  ID AS CAST(1 AS tinyint),  -- or: AS bit
  ...  -- other columns
  CONSTRAINT UQ_Configuration_ID UNIQUE (ID)
);
Andriy M
la source
9

Vous pouvez également utiliser le déclencheur.

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

    if @tableCount > 50
    begin
        rollback
    end
go
topher
la source
1

Semble un peu une exigence étrange mais ho-hum :) Vous pourriez simplement avoir une contrainte sur la table et ensuite autoriser uniquement les mises à jour (pas d'insertion ou de suppression) de la table?

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

C'est un peu un moyen hackey de le faire, ne serait-il pas préférable d'appliquer simplement des changements à la configuration via une procédure stockée qui peut alors gérer toute cette logique pour vous?

Tapis
la source
2
Assurez-vous que personne ne peut supprimer de la table. Si quelqu'un supprime puis essaie de réinsérer, il essaiera d'insérer avec une identité de 2 qu'il ne permettra pas.
Mat
5
Cela n'empêche pas IDd'avoir une valeur 0ou une valeur négative. Et en tant que points @Mat, il échouera si vous essayez d'insérer une autre ligne si la première est supprimée.
ypercubeᵀᴹ
2
Quant à être une "exigence étrange", je préfère utiliser une table à une seule ligne pour les paramètres de configuration, au lieu de la conception EAV apparemment plus courante . L'avantage du premier étant que les colonnes peuvent être créées avec un type de données approprié et des contraintes appropriées peuvent être ajoutées (plus facilement).
Kenny Evitt
2
Je n'ai peut-être pas été très clair dans mon commentaire précédent. Un effet secondaire de «Cela n'interdit pas à l'ID d'avoir une valeur de 0 ou une valeur négative» est que la table peut se terminer par 2 lignes ou plus. La propriété d'identité n'implique pas de contrainte unique.
ypercubeᵀᴹ
3
Pour illustrer ce que @ ypercubeᵀᴹ a dit, avec cette solution, vous pouvez faire par exemple INSERT INTO dbo.Config DEFAULT VALUES;une seule fois, mais vous pouvez la suivre SET IDENTITY_INSERT dbo.Config ON; INSERT INTO dbo.Config (ID) VALUES (0); SET IDENTITY_INSERT dbo.Config OFF; plusieurs fois et vous vous retrouverez avec un tableau à plusieurs lignes.
Andriy M