2010-11-01 26 views
17

Así que estaba tratando de resolver esto, pero parece que la última línea (el cheque) no permite sub consultas en ella. ¿Alguna forma de hacer que esto funcione Oracle?Usando subconsulta en una declaración de verificación en Oracle

CREATE TABLE Tank (
    n_id   int, 
    day    date, 
    level   int, 
    CONSTRAINT pk_w_td PRIMARY KEY (n_id,day), 
    CONSTRAINT fk_w_td_tan FOREIGN KEY (n_id) REFERENCES Tanks ON DELETE CASCADE, 
    CHECK (level > 0 AND level <= (SELECT capacity FROM Tanks WHERE Tanks.n_id = TanksDay.n_id)) 
); 

Aquí está la información de error:

Error at Command Line:7 Column:32 Error report: SQL Error: ORA-02251: subquery not allowed here 
02251. 00000 - "subquery not allowed here" 
*Cause: Subquery is not allowed here in the statement. 
*Action: Remove the subquery from the statement. 
+1

Excellent question. Las restricciones generales de tabla cruzada (además de las restricciones FK) son una de las características que más me gustaría ver añadidas a Oracle. –

Respuesta

22

Existen tres formas básicas de resolver este tipo de problema ya que las restricciones CHECK no se pueden basar en una consulta.

Opción 1: activa

El enfoque más simplista sería poner un disparador en el tanque que consulta tanques y lanza una excepción si el nivel excede la capacidad. El problema con este tipo de enfoque simplista, sin embargo, es que es casi imposible manejar problemas de concurrencia correctamente. Si la sesión 1 disminuye la CAPACIDAD, luego la sesión 2 aumenta el NIVEL, y luego ambas transacciones se comprometen, los disparadores no podrán detectar la violación. Esto puede no ser un problema si una o ambas tablas rara vez se modifican, pero en general va a ser un problema.

Opción 2: Las vistas materializadas

Puede resolver el problema de concurrencia mediante la creación de una ON COMMIT opinión de que se une a la mesa TANK y tanques se materializó y luego crear una restricción CHECK en la vista materializada que verifica que el nivel < = CAPACIDAD. También puede evitar almacenar los datos dos veces haciendo que la vista materializada contenga solo datos que violarían la restricción. Esto requerirá registros de vista materializados en ambas tablas base que agregarán un poco de sobrecarga a las inserciones (aunque menos que el uso de activadores). Al presionar el botón Check para comprometer el tiempo se resolverá el problema de concurrencia pero introduce un poco de un problema de administración de excepciones ya que la operación COMMIT ahora puede fallar debido a que la actualización de la vista materializada falló. Su aplicación debería ser capaz de manejar ese problema y alertar al usuario de ese hecho.

Opción 3: Cambiar el modelo de datos

Si usted tiene un valor en el cuadro A que depende de un límite en el cuadro B, que puede indicar que el límite en B debe ser un atributo de la tabla A (en lugar de o además de ser un atributo de la tabla B). Depende de las características específicas de su modelo de datos, por supuesto, pero a menudo vale la pena considerarlo.

7

Sin lamentablemente VER limitaciones no pueden contener subconsultas - ver documentation.

0

Probablemente necesitará crear activadores y usar RAISE_APPLICATION_ERROR si está fuera del rango permitido.

+0

Y otra cosa a tener en cuenta al crear un disparador, es posible que desee un desencadenador en la capacidad de retención de la tabla para asegurarse de que nunca se hizo más pequeño que el nivel máximo. –

1

La respuesta de Justin tiene algunas buenas ideas. Otra es envolver todas las inserciones/actualizaciones en la tabla con un paquete (un TAPI, si se quiere), e implementar los controles allí. Deberá asegurarse de que todas las aplicaciones utilicen su TAPI. También deberá implementar algún bloqueo personalizado para proteger la restricción de los efectos de la actividad concurrente.

Cuestiones relacionadas