NO DEFERRABLE versus DEFERRABLE INICIALMENTE INMEDIATO

90

Leí esto sobre la palabra clave SQL DEFERRABLEen Database Systems - The Complete Book .

El último [NO DEFERRABLE] es el predeterminado y significa que cada vez que se ejecuta una instrucción de modificación de la base de datos, la restricción se verifica inmediatamente después, si la modificación podría violar la restricción de clave externa.

Sin embargo, si declaramos que una restricción es DEFERRABLE , entonces tenemos la opción de hacer que espere hasta que se complete una transacción antes de verificar la restricción.

Seguimos la palabra clave DEFERRABLE por cualquiera INICIALMENTE DIFERIDO o INICIALMENTE INMEDIATA . En el primer caso, la verificación se aplazará justo antes de que se confirme cada transacción. En este último caso, la verificación se realizará inmediatamente después de cada extracto.

¿En qué se NOT DEFERRABLEdiferencia de DEFERRABLE INITIALLY IMMEDIATE? En ambos casos, al parecer, las restricciones se verifican después de cada declaración individual.

Pieter
fuente

Respuestas:

72

Con DEFERRABLE INITIALLY IMMEDIATEpuede diferir las restricciones bajo demanda cuando lo necesite.

Esto es útil si normalmente desea verificar las restricciones en el momento de la declaración, pero, por ejemplo, una carga por lotes desea posponer la verificación hasta el momento de la confirmación.

Sin embargo, la sintaxis de cómo diferir las restricciones es diferente para los distintos DBMS.

Con NOT DEFERRABLEusted nunca podrá aplazar la verificación hasta el momento del compromiso.

un caballo sin nombre
fuente
1
@romkyns: DEFERRABLEestablece la intención del diseñador de que diferir la restricción es una acción que vale la pena o es necesaria. Este no es el caso de la gran mayoría de las limitaciones de la base de datos y el etiquetado de todos, ya DEFERRABLEque perdería esta útil distinción.
cuando
4
@onedaywhen Un colega mío ha señalado una buena razón válida, en realidad: puede confiar en restricciones no diferibles en cualquier punto de cualquier transacción, pero las diferibles solo se observan definitivamente al comienzo de una transacción.
Roman Starkov
@RomanStarkov Además, es una cuestión de rendimiento. NOT DEFERRABLEsuele ser el más rápido.
Teejay
46

Aparte de las otras respuestas (correctas), cuando se habla de PostgreSQL , se debe decir que:

  • con NOT DEFERRABLE cada fila se marca en el momento de la inserción / actualización

  • con DEFERRABLE (actualmente INMEDIATO ) todas las filas se verifican al final de la inserción / actualización

  • con DEFERRABLE (actualmente DEFERRED ) todas las filas se verifican al final de la transacción

Por lo tanto , no es correcto decir que una restricción DEFERRABLE actúa como una NO DEFERRABLE cuando se establece en INMEDIATO.


Desarrollemos esta diferencia:

CREATE TABLE example(
    row integer NOT NULL,
    col integer NOT NULL,
    UNIQUE (row, col) DEFERRABLE INITIALLY IMMEDIATE
);

INSERT INTO example (row, col) VALUES (1,1),(2,2),(3,3);

UPDATE example SET row = row + 1, col = col + 1;

SELECT * FROM example;

Esto produce correctamente:

salida

Pero si eliminamos la instrucción DEFERRABLE INICIALMENTE INMEDIATA,

ERROR: el valor de clave duplicado viola la restricción única "example_row_col_key" DETALLE: La clave ("fila", col) = (2, 2) ya existe. ********** Error **********

ERROR: el valor de clave duplicado viola la restricción única "example_row_col_key" Estado SQL: 23505 Detalle: La clave ("fila", col) = (2, 2) ya existe.


ADENDA (12 de octubre de 2017)

De hecho, este comportamiento está documentado aquí , sección "Compatibilidad":

Además, PostgreSQL verifica las restricciones de unicidad no diferibles inmediatamente, no al final de la declaración como sugiere el estándar.

Teejay
fuente
Interesante. Me parece que básicamente no hay razón para preferir el comportamiento "NO DEFERRABLE" al comportamiento "INMEDIATO" y que tendría sentido para Postgres ser más indulgente y hacer que NO DEFERRABLE se comporte como "INMEDIATO". El orden en el que se actualizan las filas en una declaración UPDATE ni siquiera está documentado o especificado hasta donde yo sé, entonces, ¿el comportamiento de esta verificación de restricciones de declaración intermedia no está al menos parcialmente no especificado? No veo por qué alguien querría ese comportamiento, pero tal vez haya un caso de uso razonable para él que no tengo la imaginación para ver.
Mark Amery
1
Sí, básicamente la única razón para preferir NOT DEFERRABLEes la velocidad ( consulte aquí , sección Restricciones de unicidad no diferidas , "Tenga en cuenta que esto puede ser significativamente más lento que la comprobación de unicidad inmediata" ).
Teejay
Además, la DEFERRABLErestricción no puede ser referenciada como claves foráneas en otras tablas ( ver aquí , sección Parámetros , "Las columnas referenciadas deben ser las columnas de una restricción de clave primaria o única no diferible en la tabla referenciada" ).
Teejay
1
Entonces, como regla general, si realmente no le importa a la lógica empresarial cuando se verifica la restricción, ¿debería usar de forma predeterminada NOT DEFERRABLEsimplemente porque funciona mejor?
dvtan
1
En nuestro ORM usamos ahora las siguientes reglas: 1) si la restricción es una PK, usamos NOT DEFERRABLE2) si al menos una FK hace referencia a la restricción, usamos NOT DEFERRABLEtambién 3) en los otros casos, usamos DEFERRABLE INITIALLY IMMEDIATE. Esto podría disminuir ligeramente el rendimiento de esas restricciones, pero asegura la máxima compatibilidad con otros DBMS que usamos (Oracle, SqlServer). PK y FK no son un problema porque nunca actualizamos sus valores (lo que creo que es un buen hábito de programación para DB).
Teejay
29

Aparte de lo obvio de poder aplazar, la diferencia es en realidad el rendimiento. Si no hubiera una penalización de desempeño, entonces no habría necesidad de tener la opción de elegir diferible o no; todas las restricciones simplemente serían diferibles.

La penalización del rendimiento tiene que ver con las optimizaciones que la base de datos puede realizar dado el conocimiento de cómo se restringen los datos. Por ejemplo, el índice que se crea para respaldar una restricción única en Oracle no puede ser un índice único si la restricción es diferible, ya que se debe permitir el permiso temporal de duplicados. Sin embargo, si la restricción no se puede aplazar, el índice puede ser único.

Ryan
fuente
7

Llego muy tarde a la fiesta, pero quería agregar que, a partir de diciembre de 2018, solo dos bases de datos que conozco (puede haber más) ofrecen algún nivel de implementación de esta característica estándar de SQL :

Database    NOT DEFERRABLE  DEFERRABLE           DEFERRABLE 
                            INITIALLY IMMEDIATE  INITIALLY DEFERRED
----------  --------------  -------------------  ------------------
Oracle      N/A *1          Yes (default)        Yes
PostgreSQL  Yes (default)   Yes                  Yes
DB2         -               -                    -
SQL Server  -               -                    -
MySQL       -               -                    -
MariaDB     -               -                    -
SAP Sybase  -               -                    -
HyperSQL    -               -                    -
H2          -               -                    -
Derby       -               -                    -

* 1 Aunque Oracle 12c acepta el NOT DEFERRABLE estado de restricción , en realidad lo ignora y lo hace funcionar comoDEFERRABLE INITIALLY IMMEDIATE .

Como ve, Oracle no implementa el primer tipo (NOT DEFERRABLE ), y es por eso que los desarrolladores que usan Oracle (el OP en este caso) pueden confundirse y considerar los dos primeros tipos equivalentes.

Curiosamente, Oracle y PostgreSQL tienen un tipo predeterminado diferente. Quizás tenga implicaciones en el rendimiento.

El Empalador
fuente
4

NO DEFERRABLE: no puede cambiar la comprobación de restricciones, Oracle la comprueba después de cada declaración (es decir, directamente después de insertar la declaración).

DEFERRABLE INICIALMENTE INMEDIATO: Oracle verifica la restricción después de cada declaración. PERO, puede cambiarlo después de cada transacción (es decir, después de confirmar):

set constraint pk_tab1 deferred;
hajili
fuente