NON DEFERRABLE versus DEFERRABLE INITIALEMENT IMMEDIATE

90

J'ai lu ceci à propos du mot-clé SQL DEFERRABLEdans Database Systems - The Complete Book .

Le dernier [NOT DEFERRABLE] est la valeur par défaut, et signifie que chaque fois qu'une instruction de modification de base de données est exécutée, la contrainte est vérifiée immédiatement après, si la modification peut violer la contrainte de clé étrangère.

Cependant, si nous déclarons qu'une contrainte est DEFERRABLE , nous avons la possibilité de la faire attendre qu'une transaction soit terminée avant de vérifier la contrainte.

Nous suivons le mot-clé DEFERRABLE soit INITIALLY DEFERRED soit INITIALLY IMMEDIATE . Dans le premier cas, la vérification sera reportée juste avant la validation de chaque transaction. Dans ce dernier cas, le contrôle sera effectué immédiatement après chaque relevé.

En quoi est-ce NOT DEFERRABLEdifférent de DEFERRABLE INITIALLY IMMEDIATE? Dans les deux cas, il semble que toutes les contraintes sont vérifiées après chaque déclaration individuelle.

Pieter
la source

Réponses:

72

Avec DEFERRABLE INITIALLY IMMEDIATEvous pouvez reporter les contraintes à la demande lorsque vous en avez besoin.

Ceci est utile si vous voulez normalement vérifier les contraintes au moment de l'instruction, mais par exemple, pour un chargement par lots, vous voulez reporter la vérification jusqu'au moment de la validation.

La syntaxe pour différer les contraintes est cependant différente pour les différents SGBD.

Avec, NOT DEFERRABLEvous ne pourrez jamais différer la vérification jusqu'au moment de la validation.

un cheval sans nom
la source
1
@romkyns: DEFERRABLEindique l'intention du concepteur selon laquelle le report de la contrainte est une action valable ou nécessaire. Ce n'est pas le cas pour la grande majorité des contraintes de base de données et l'étiquetage du tout car DEFERRABLEcela perdrait cette distinction utile.
jour du
4
@onedaywhen Un de mes collègues a depuis souligné une bonne raison valable, en fait: vous pouvez vous fier à des contraintes non reportables à tout moment de n'importe quelle transaction, mais celles reportables ne sont définitivement observées qu'au début d'une transaction.
Roman Starkov
@RomanStarkov C'est aussi une question de performance. NOT DEFERRABLEest généralement le plus rapide.
Teejay
46

Mis à part les autres réponses (correctes), quand on parle de PostgreSQL , il faut dire que:

  • avec NOT DEFERRABLE chaque ligne est vérifiée lors de l'insertion / mise à jour

  • avec DEFERRABLE (actuellement IMMEDIATE ) toutes les lignes sont vérifiées à la fin de l'insertion / mise à jour

  • avec DEFERRABLE (actuellement DEFERRED ) toutes les lignes sont vérifiées à la fin de la transaction

Il n'est donc pas correct de dire qu'une contrainte DEFERRABLE agit comme une contrainte NOT DEFERRABLE lorsqu'elle est définie sur IMMEDIATE.


Développons cette différence:

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;

Cela génère correctement:

production

Mais si nous supprimons l'instruction DEFERRABLE INITIALLY IMMEDIATE,

ERREUR: la valeur de clé en double enfreint la contrainte unique "example_row_col_key" DETAIL: Key ("row", col) = (2, 2) existe déjà. ********** Erreur **********

ERREUR: la valeur de clé en double viole la contrainte unique "example_row_col_key" État SQL: 23505 Détail: La clé ("row", col) = (2, 2) existe déjà.


ADDENDA (12 octobre 2017)

Ce comportement est en effet documenté ici , section "Compatibilité":

De plus, PostgreSQL vérifie immédiatement les contraintes d'unicité non reportables, pas à la fin de l'instruction comme le suggère la norme.

Teejay
la source
Intéressant. Il me semble qu'il n'y a fondamentalement aucune raison de préférer le comportement "NOT DEFERRABLE" au comportement "IMMEDIATE" et qu'il serait logique pour Postgres d'être plus indulgent et de faire en sorte que NOT DEFERRABLE se comporte comme "IMMEDIATE". L'ordre dans lequel les lignes sont mises à jour dans une instruction UPDATE n'est même pas documenté ou spécifié pour autant que je sache, le comportement de cette vérification de contrainte de mi-instruction n'est donc pas au moins partiellement non spécifié? Je ne vois pas pourquoi quelqu'un voudrait ce comportement - mais il y a peut-être un cas d'utilisation raisonnable pour cela que je manque d'imagination pour voir.
Mark Amery
1
Oui, au fond, la seule raison de préférer NOT DEFERRABLEest la vitesse ( voir ici , section Contraintes d'unicité non différées , «Sachez que cela peut être beaucoup plus lent que la vérification d'unicité immédiate» ).
Teejay
De plus, la DEFERRABLEcontrainte ne peut pas être référencée en tant que clé étrangère dans d'autres tables ( voir ici , section Paramètres , "Les colonnes référencées doivent être les colonnes d'une contrainte de clé primaire ou unique non déferrable dans la table référencée" ).
Teejay
1
Donc, en règle générale, si cela n'a pas vraiment d'importance pour la logique métier lorsque la contrainte est vérifiée, devrais-je utiliser par défaut NOT DEFERRABLEsimplement parce qu'elle fonctionne mieux?
dvtan
1
Dans notre ORM nous utilisons maintenant les règles suivantes: 1) si la contrainte est un PK, nous utilisons NOT DEFERRABLE2) si au moins un FK fait référence à la contrainte, nous utilisons NOT DEFERRABLEégalement 3) dans les autres cas, nous utilisons DEFERRABLE INITIALLY IMMEDIATE. Cela pourrait légèrement diminuer les performances de ces contraintes, mais assure une compatibilité maximale avec les autres SGBD que nous utilisons (Oracle, SqlServer). PK et FK ne sont pas un problème car nous ne mettons jamais à jour leurs valeurs (ce qui, je pense, est une bonne habitude de programmation pour les bases de données).
Teejay
29

Outre l'évidence de pouvoir différer, la différence réside en fait dans les performances. S'il n'y avait pas de pénalité de performance, il ne serait pas nécessaire d'avoir la possibilité de choisir de différer ou non - toutes les contraintes seraient simplement reportables.

La pénalité des performances est liée aux optimisations que la base de données peut effectuer étant donné la connaissance de la manière dont les données sont restreintes. Par exemple, l'index qui est créé pour sauvegarder une contrainte unique dans Oracle ne peut pas être un index unique si la contrainte est reportable, car l'autorisation temporaire des doublons doit être autorisée. Cependant, si la contrainte n'est pas reportable, l'index peut être unique.

Ryan
la source
7

Je suis très en retard à la fête mais je voulais ajouter que - en décembre 2018 - seules deux bases de données que je connais (il y en a peut-être plus) offrent un certain niveau d'implémentation de cette fonctionnalité SQL standard :

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 Même si Oracle 12c accepte l' NOT DEFERRABLE état de contrainte , il l'ignore en fait et le fait fonctionner comme DEFERRABLE INITIALLY IMMEDIATE.

Comme vous le voyez, Oracle n'implémente pas le premier type ( NOT DEFERRABLE), et c'est pourquoi les développeurs utilisant Oracle (l'OP dans ce cas) peuvent être confus et considérer les deux premiers types comme équivalents.

Fait intéressant, Oracle et PostgreSQL ont un type par défaut différent. Cela a peut-être des implications sur les performances.

L'Impaler
la source
4

NOT DEFERRABLE - vous ne pouvez pas modifier la vérification des contraintes, oracle la vérifie après chaque instruction (c'est-à-dire directement après l'instruction insert).

DEFERRABLE INITIALLY IMMEDIATE - oracle vérifie la contrainte après chaque instruction. MAIS, vous pouvez le changer après chaque transaction (c'est-à-dire après la validation):

set constraint pk_tab1 deferred;
hajili
la source