SQL Server: ¿cómo restringir una tabla para que contenga una sola fila?

81

Quiero almacenar una sola fila en una tabla de configuración para mi aplicación. Me gustaría hacer cumplir que esta tabla solo puede contener una fila.

¿Cuál es la forma más sencilla de aplicar la restricción de una sola fila?

Martín
fuente
¿Por qué no utilizar una tabla con columnas (Name, Value)con una clave principal en el nombre? Entonces puede tener la select Value from Table where Name = ?certeza de que no se devolverá ninguna fila o una sola.
2010
2
No estoy seguro de que sql sea la mejor solución aquí. Quizás un archivo xml simple sea más apropiado para la configuración. Solía ​​pensar que la configuración! = Data y sql se hizo para datos.
remi bourgarel
2
@ar: he visto que eso va muy mal cuando espera leer, digamos, un entero, y obtiene un valor mal formateado en la columna de valor.
Damien_The_Unbeliever
@Damien_The_Unbeliever ¿Por qué pasaría eso? ¿Porque especificaste un valor inexistente para Name?
Noumenon
1
@Noumenon: tenga en cuenta que mi comentario fue una respuesta al arcomentario s. El problema es que, si solo está almacenando pares de nombre / valor, el valor tiene que ser una cadena y no tiene forma de hacer cumplir la validación en la base de datos. Cuando usa una tabla de una sola fila con columnas separadas para cada configuración (como lo deseaba el OP), puede aplicar fácilmente la validación para cada configuración a través de restricciones de verificación.
Damien_The_Unbeliever

Respuestas:

97

Asegúrese de que una de las columnas solo pueda contener un valor y luego conviértalo en la clave principal (o aplique una restricción de unicidad).

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

Tengo varias de estas tablas en varias bases de datos, principalmente para almacenar config. Es mucho más agradable saber que, si el elemento de configuración debe ser un int, solo leerá un int de la base de datos.

Damien_The_Unbeliever
fuente
2
+1. Este es el enfoque que usa Celko para una tabla de constantes auxiliares en "SQL para Smarties"
Martin Smith
Esto es tan simple como parece. Gracias.
Martin
+1: solución interesante. No había visto esto antes, así que gracias por compartir. Siempre el camino, fácil cuando sabes cómo ...
John Sansom
Aquí hay una pregunta de seguimiento para pensar. ¿Cuál es la clave principal de esta tabla? :)
nvogel
2
@BZ - cdt es la abreviatura de comp.databases.theoryun grupo de Usenet (visible a través de grupos de Google) que admito que no he leído mucho recientemente. Estaba más orientado a la teoría relacional que a SQL, pero sabía que dportas / sqlvogel también frecuentaba el mismo grupo. TTM era una referencia a The Third Manifesto , que es un buen libro que habla (de nuevo) sobre teoría relacional en lugar de SQL.
Damien_The_Unbeliever
53

Normalmente uso el enfoque de Damien, que siempre me ha funcionado muy bien, pero también agrego una cosa:

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

Añadiendo la "X" POR DEFECTO ", nunca tendrá que lidiar con la columna Bloquear, y no tendrá que recordar cuál fue el valor de bloqueo cuando cargue la tabla por primera vez.

ACB
fuente
4
La restricción predeterminada también debe ser nombrada o de lo contrario obtendrá un nombre confuso generado automáticamente. Lock char(1) not null CONSTRAINT DF_T1_Lock DEFAULT 'X'
David S.
1
Además, debe hacer que su valor predeterminado sea diferente a 'X'. Hice la mía una cadena más larga, y este es mi mensaje de error ahora si intento insertar una segunda fila: Violación de la restricción PRIMARY KEY 'PK_RestrictToOneRow'. No se puede insertar una clave duplicada en el objeto 'dbo.1ROWTABLE'. El valor de la clave duplicada es (Esta tabla está bloqueada en una fila).
Geoff Griswald
14

Es posible que desee reconsiderar esta estrategia. En situaciones similares, a menudo me ha resultado invaluable dejar las filas de configuración antiguas por ahí para obtener información histórica.

Para hacer eso, en realidad tiene una columna adicional creation_date_time(fecha / hora de inserción o actualización) y un activador de inserción o inserción / actualización que la completará correctamente con la fecha / hora actual.

Luego, para obtener su configuración actual, usa algo como:

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

(dependiendo de su sabor DBMS).

De esa manera, aún puede mantener el historial para fines de recuperación (puede instituir procedimientos de limpieza si la tabla se vuelve demasiado grande, pero esto es poco probable) y aún puede trabajar con la última configuración.

paxdiablo
fuente
+1: Escucho lo que está diciendo, pero prefiero registrar el historial de cambios en tablas de auditoría separadas.
Martin
+1: Por compartir la interesante idea de implementar una pista de auditoría.
John Sansom
Agradable. JustSELECT TOP 1 ... ORDER BY creation_date_time DESC
Baodad
5

Se puede implementar un LUGAR DE disparador para hacer cumplir este tipo de lógica de negocio dentro de la base de datos.

El disparador puede contener lógica para verificar si un registro ya existe en la tabla y, de ser así, ROLLBACK el Insert.

Ahora, dando un paso atrás para ver el panorama general, me pregunto si tal vez existe una forma alternativa y más adecuada para almacenar esta información, tal vez en un archivo de configuración o variable de entorno, por ejemplo.

John Sansom
fuente
+1 - Puedo ver cómo funcionaría el disparador, y tal vez recurriré a ese enfoque, pero me gusta la simple restricción de Damien. Hay una discusión interesante sobre qué tipo de datos de configuración pertenecen al archivo de configuración y qué pertenecen a la base de datos. En este caso, creo que el DB es el lugar correcto. Sin duda viviré para lamentar esto ... :-)
Martin
2

Utilizo un campo de bits para la clave principal con el nombre IsActive. Entonces, puede haber 2 filas como máximo y el sql para obtener la fila válida es: seleccione * de Configuración donde IsActive = 1 si la tabla se llama Configuración.

usuario3382925
fuente
2

Aquí hay una solución que se me ocurrió para una tabla de tipo de bloqueo que puede contener solo una fila, con una Y o N (un estado de bloqueo de la aplicación, por ejemplo).

Crea la tabla con una columna. Puse una restricción de verificación en una columna para que solo se pueda poner una Y o una N en ella. (O 1 o 0, o lo que sea)

Inserte una fila en la tabla, con el estado "normal" (por ejemplo, N significa no bloqueado)

Luego, cree un desencadenador INSERT en la tabla que solo tenga una SEÑAL (DB2) o RAISERROR (SQL Server) o RAISE_APPLICATION_ERROR (Oracle). Esto hace que el código de la aplicación pueda actualizar la tabla, pero cualquier INSERT falla.

Ejemplo de 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';

Funciona para mi.

David A Beamer
fuente
2

Pregunta antigua, pero ¿qué tal si se usa IDENTITY (MAX, 1) de un tipo de columna pequeña?

CREATE TABLE [dbo].[Config](
[ID] [tinyint] IDENTITY(255,1) NOT NULL,
[Config1] [nvarchar](max) NOT NULL,
[Config2] [nvarchar](max) NOT NULL
NeutronCode
fuente
Puede agregar otra fila usando SET IDENTITY_INSERT.
Razvan Socol
1

Puede escribir un disparador en la acción de inserción en la tabla. Siempre que alguien intente insertar una nueva fila en la tabla, elimine la lógica de eliminar la última fila en el código de activación de inserción.

Karan
fuente
-1
IF NOT EXISTS ( select * from table )
BEGIN
    ///Your insert statement
END
Sachin Shanbhag
fuente
-1

Aquí también podemos hacer un valor invisible que será el mismo después de la primera entrada en la base de datos.Ejemplo: Student Table: Id: int firstname: char Aquí en el cuadro de entrada, tenemos que especificar el mismo valor para la columna id que restringirá como después de la primera entrada que no sea la escritura de bloqueo bla bla debido a la restricción de la clave principal, por lo que solo tiene una fila para siempre. ¡Espero que esto ayude!

Yishagerew
fuente