web-dev-qa-db-fra.com

SQL Server: comment contraindre une table à contenir une seule ligne?

Je souhaite stocker une seule ligne dans une table de configuration pour mon application. Je voudrais faire valoir que ce tableau ne peut contenir qu'une seule ligne.

Quelle est la manière la plus simple d'appliquer la contrainte de ligne unique?

75
Martin

Vous vous assurez qu'une des colonnes ne peut contenir qu'une seule valeur, puis vous en faites la clé primaire (ou appliquez une contrainte d'unicité).

CREATE TABLE T1(
    Lock char(1) not null,
    /* Other columns */,
    constraint PK_T1 PRIMARY KEY (Lock),
    constraint CK_T1_Locked CHECK (Lock='X')
)

J'ai un certain nombre de ces tables dans diverses bases de données, principalement pour stocker la configuration. C'est beaucoup plus agréable de savoir que, si l'élément de configuration doit être un int, vous ne lirez que l'int de la base de données.

87

J'utilise généralement l'approche de Damien, qui a toujours très bien fonctionné pour moi, mais j'ajoute également une chose:

CREATE TABLE T1(
    Lock char(1) not null DEFAULT 'X',
    /* Other columns */,
    constraint PK_T1 PRIMARY KEY (Lock),
    constraint CK_T1_Locked CHECK (Lock='X')
)

En ajoutant le "DEFAULT 'X'", vous n'aurez jamais à vous occuper de la colonne Lock, et vous n'aurez pas à vous rappeler quelle était la valeur de verrouillage lors du chargement de la table pour la première fois.

47
ACB

Vous voudrez peut-être repenser cette stratégie. Dans des situations similaires, j'ai souvent trouvé inestimable de laisser les anciennes lignes de configuration traîner pour des informations historiques.

Pour ce faire, vous disposez en fait d'une colonne supplémentaire creation_date_time (date/heure d'insertion ou de mise à jour) et un déclencheur d'insertion ou d'insertion/mise à jour qui le remplira correctement avec la date/heure actuelle.

Ensuite, afin d'obtenir votre configuration actuelle, vous utilisez quelque chose comme:

select * from config_table order by creation_date_time desc fetch first row only

(en fonction de votre saveur SGBD).

De cette façon, vous pouvez toujours conserver l'historique à des fins de récupération (vous pouvez instituer des procédures de nettoyage si la table devient trop grande mais cela est peu probable) et vous pouvez toujours travailler avec la dernière configuration.

14
paxdiablo

Vous pouvez implémenter un INSTEAD OF Trigger pour appliquer ce type de logique métier dans la base de données.

Le déclencheur peut contenir une logique pour vérifier si un enregistrement existe déjà dans la table et si c'est le cas, ROLLBACK l'insertion.

Maintenant, en prenant un peu de recul pour regarder la situation dans son ensemble, je me demande s'il existe peut-être un moyen alternatif et plus approprié pour stocker ces informations, peut-être dans un fichier de configuration ou une variable d'environnement par exemple?

5
John Sansom

Voici une solution que j'ai trouvée pour une table de type verrou qui ne peut contenir qu'une seule ligne, contenant un Y ou un N (un état de verrouillage d'application, par exemple).

Créez le tableau avec une colonne. J'ai mis une contrainte de vérification sur une colonne afin que seul un Y ou un N puisse y être mis. (Ou 1 ou 0, ou autre)

Insérez une ligne dans le tableau, avec l'état "normal" (par exemple, N signifie non verrouillé)

Créez ensuite un déclencheur INSERT sur la table qui n'a qu'un SIGNAL (DB2) ou RAISERROR (SQL Server) ou RAISE_APPLICATION_ERROR (Oracle). Cela permet au code d'application de mettre à jour la table, mais tout INSERT échoue.

Exemple DB2:

create table PRICE_LIST_LOCK
(
    LOCKED_YN       char(1)   not null  
        constraint PRICE_LIST_LOCK_YN_CK  check (LOCKED_YN in ('Y', 'N') )
);
--- do this insert when creating the table
insert into PRICE_LIST_LOCK
values ('N');

--- once there is one row in the table, create this trigger
CREATE TRIGGER ONLY_ONE_ROW_IN_PRICE_LIST_LOCK
   NO CASCADE 
   BEFORE INSERT ON PRICE_LIST_LOCK
   FOR EACH ROW
   SIGNAL SQLSTATE '81000'  -- arbitrary user-defined value
     SET MESSAGE_TEXT='Only one row is allowed in this table';

Travaille pour moi.

2
David A Beamer

J'utilise un champ de bits pour la clé primaire avec le nom IsActive. Il peut donc y avoir 2 lignes au maximum et et le sql pour obtenir la ligne valide est: sélectionnez * dans Paramètres où IsActive = 1 si le tableau est nommé Paramètres.

2
user3382925

Vous pouvez écrire un déclencheur sur l'action d'insertion sur la table. Chaque fois que quelqu'un essaie d'insérer une nouvelle ligne dans le tableau, lancez la logique de suppression de la dernière ligne du code de déclenchement d'insertion.

1
Karan

Ancienne question, mais que diriez-vous d'utiliser IDENTITY (MAX, 1) d'un type à petite colonne?

CREATE TABLE [dbo].[Config](
[ID] [tinyint] IDENTITY(255,1) NOT NULL,
[Config1] [nvarchar](max) NOT NULL,
[Config2] [nvarchar](max) NOT NULL
1
NeutronCode
IF NOT EXISTS ( select * from table )
BEGIN
    ///Your insert statement
END
0
Sachin Shanbhag

Ici, nous pouvons également créer une valeur invisible qui sera la même après la première entrée dans la base de données. comme après la première entrée autre que l'écriture du verrou bla bla en raison de la contrainte de clé primaire n'ayant ainsi qu'une seule ligne pour toujours. J'espère que cela t'aides!

0
Yishagerew