2009-06-06 12 views
10

En SQL, ¿hay alguna forma de exigir que solo una columna de un grupo de columnas tenga un valor y las demás sean nulas? ¿Tal vez una restricción o un disparador? Este tipo de cosas podría ser para una tabla de búsqueda, pero ¿hay algún diseño de tabla alternativo que pueda lograr esto mejor?SQL: cómo hacer cumplir que solo se establece una sola columna en un grupo de columnas

Por ejemplo:

ID OtherTable1ID OtherTable2ID OtherTable3ID 
----------------------------------------------------- 
1  23    NULL    NULL 
2  NULL    45    NULL 
3  33    55    NULL -- NOT ALLOWED 

El principal problema es que estas columnas son todas las claves ajenas a otras tablas, por lo que no puede caer hacia abajo a una sola columna.

Estoy usando SQL Server, pero cualquier respuesta servirá.

+0

Gracias por la actualización, pero es posible que desee dar más detalles sobre el diseño de la base de datos. Por ejemplo, esta es una situación de subtipado, donde esta tabla puede ser un subtipo de una y solo una de las otras tablas. Además, ¿está permitido que todas las columnas "Otras" sean nulas? –

+0

Parece una tabla de búsqueda. ¿Básicamente significa que si agregas un FK más, le agregas una columna más? –

+0

Sí, básicamente es una tabla de búsqueda para mí. Si necesito agregar otro parámetro de búsqueda, tendría que agregar otra columna de FK. ¿Hay mejores formas de lograr esto? –

Respuesta

13

@ limitaciones sugeridas por tvanfosson funciona bien para tres columnas, pero para la generalidad prefiero

(cast(col1 is not null, int) + 
cast(col2 is not null, int) + 
cast(col3 is not null, int)) = 1 

porque generaliza mejor a cualquier número de columnas con "linealmente creciente" (en lugar de "cuadráticamente creciente") cantidad de codificación (es incluso más preciso en los dialectos de SQL que no requieren una conversión explícita de bit booleano aka a int, pero no estoy seguro si SQL Server es uno de ellos).

+4

Para SQL Server, esto funcionaría mejor ... (caso cuando col1 IS NOT NULL then 1 else 0 end + case cuando col2 IS NOT NULL then 1 else 0 end <2) –

+1

@Glen Little: Estoy de acuerdo con la sintaxis ofrecida, pero creo que la comparación para el caso del OP aún debe ser '... = 1'. –

6

Una restricción como la siguiente debería funcionar:

(column1 is null and column2 is null) 
    or (column1 is null and column3 is null) 
    or (column2 is null and column3 is null) 

Esto no forzarlo a contener una columna no nulo, sin embargo. Para hacer que añadir otra restricción:

column1 is not null 
    or column2 is not null 
    or column3 is not null 
0

Me suena como si quieres estar usando una columna para el conjunto de esas cosas. ¿Quizás pueda usar algún tipo de etiqueta para decir que es Foo,3 o Bar,7 o Baz,9?

+0

Debería haber aclarado esto en mi pregunta, pero el problema que estoy enfrentando es que las columnas son FK a otras tablas. Permítanme actualizar mi pregunta, lo siento –

+0

¡Ah, ahora se pone interesante! Déjame pensar en esto un poco, pero parece que es básicamente un problema con una implementación de RDBMS deficiente. Desafortunadamente, en este sentido, no creo que haya uno bueno por ahí ... –

+0

(Para aclarar, no hay ninguna razón por la que no se deba poder declarar que el tipo de columna es "una referencia a esto" , o una referencia a eso, o una referencia al otro, "con soporte completo de FK en todo eso, pero los DBMS actuales no llegan a ninguna parte.) –

1
CREATE TABLE Repro.Entity 
(
    entityId INTEGER IDENTITY (1, 1) NOT NULL, 
    column1 INTEGER, 
    column2 INTEGER, 
    column3 INTEGER, 
    CONSTRAINT Entity_PK PRIMARY KEY(entityId), 
    CONSTRAINT Entity_CK CHECK(
     (column1 IS NOT NULL AND column2 IS NULL AND column3 IS NULL) OR 
     (column1 IS NULL AND column2 IS NOT NULL AND column3 IS NULL) OR 
     (column1 IS NULL AND column2 IS NULL AND column3 IS NOT NULL)) 
) 
1

Para mí, parece una mala decisión de diseño. Como ID es la clave principal en esta tabla, será un valor legal para toda relación de clave externa. Esto significa que debe trabajar más duro en la capa de front-end/business para garantizar que los valores estén dentro del rango aceptado.

Por ejemplo, la forma en que se configuran las tablas, es absolutamente legal que la tabla 2 use 1 como el valor de búsqueda en lugar de 2 que se supone que debe usar, y la base de datos no lo atrapará.

Probablemente no vaya por esta ruta. Simplemente crearé un esquema llamado búsquedas y crearé una tabla de búsqueda por valor de búsqueda. De esta forma, la base de datos aplicará correctamente todas las restricciones.

La forma en que ha configurado la tabla de búsqueda, actualmente está limitado a las claves externas enteras. En algunos casos, puede que no sea una buena idea; por ejemplo, le gustaría almacenar el código/código de país para el estado en lugar de los valores enteros que los representan.

+0

Yo también prefiero evitar las columnas NULLable, pero el diseño del OP (sujeto a la restricción que funciona como se solicita) tiene una ventaja sobre la suya (asumiendo que he entendido correctamente) ya que pueden escribir una restricción para aplicar exactamente un valor no NULL mientras el suyo puede tener la situación en la que podría haber cero filas en las tablas de referencia, a menos que despliegue sus propias 'claves externas distribuidas' (google Hugh Darwen) usando activadores (o similares). – onedaywhen

Cuestiones relacionadas