NÃO DEFERRÁVEL versus DEFERRÁVEL INICIALMENTE IMEDIATO

90

Eu li sobre a palavra-chave SQL DEFERRABLEem Database Systems - The Complete Book .

O último [NOT DEFERRABLE] é o padrão e significa que toda vez que uma instrução de modificação do banco de dados é executada, a restrição é verificada imediatamente depois, se a modificação puder violar a restrição de chave estrangeira.

No entanto, se declararmos uma restrição como DEFERRABLE , temos a opção de esperar até que uma transação seja concluída antes de verificar a restrição.

Seguimos a palavra-chave DEFERRABLE por INITIALLY DEFERRED ou INITIALLY IMMEDIATE . No primeiro caso, a verificação será adiada para pouco antes de cada confirmação de transação. Neste último caso, a verificação será feita imediatamente após cada declaração.

Como é NOT DEFERRABLEdiferente de DEFERRABLE INITIALLY IMMEDIATE? Em ambos os casos, ao que parece, todas as restrições são verificadas após cada declaração individual.

Pieter
fonte

Respostas:

72

Com DEFERRABLE INITIALLY IMMEDIATEvocê pode adiar as restrições sob demanda quando precisar.

Isso é útil se você normalmente deseja verificar as restrições no momento da instrução, mas por exemplo, um carregamento em lote deseja adiar a verificação até o momento do commit.

A sintaxe de como adiar as restrições é diferente para os vários SGBDs.

Com NOT DEFERRABLEvocê, você nunca será capaz de adiar a verificação até a hora de confirmação.

um cavalo sem nome
fonte
1
@romkyns: DEFERRABLEdeclara a intenção do designer de que adiar a restrição é uma ação válida ou necessária. Este não é o caso para a grande maioria das restrições de banco de dados e rotulagem de todos, pois DEFERRABLEperderia essa distinção útil.
dia em
4
@onedaywhen Desde então, um colega meu apontou um bom motivo válido, na verdade: você pode contar com restrições não diferíveis em qualquer ponto de qualquer transação, mas aquelas que podem ser diferidas só são definitivamente observadas no início de uma transação.
Roman Starkov
@RomanStarkov Além disso, é uma questão de desempenho. NOT DEFERRABLEgeralmente é o mais rápido.
Teejay
46

Além das outras respostas (corretas), quando se fala em PostgreSQL , deve-se afirmar que:

  • com NOT DEFERRABLE, cada linha é verificada no momento da inserção / atualização

  • com DEFERRABLE (atualmente IMMEDIATE ) todas as linhas são verificadas no final da inserção / atualização

  • com DEFERRABLE (atualmente DEFERRED ) todas as linhas são verificadas no final da transação

Portanto , não é correto dizer que uma restrição DEFERRABLE age como uma NOT DEFERRABLE quando definida como IMMEDIATE.


Vamos elaborar essa diferença:

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;

Isso resulta corretamente:

resultado

Mas se removermos a instrução DEFERRABLE INITIALLY IMMEDIATE,

ERRO: o valor da chave duplicado viola a restrição exclusiva "example_row_col_key" DETALHE: A chave ("linha", col) = (2, 2) já existe. ********** Erro **********

ERRO: o valor da chave duplicado viola a restrição exclusiva "example_row_col_key" Estado SQL: 23505 Detalhe: a chave ("linha", col) = (2, 2) já existe.


ADENDO (12 de outubro de 2017)

Este comportamento está realmente documentado aqui , seção "Compatibilidade":

Além disso, o PostgreSQL verifica as restrições de exclusividade não diferíveis imediatamente, não no final da instrução como o padrão sugere.

Teejay
fonte
Interessante. Parece-me que basicamente não há razão para preferir o comportamento "NÃO DEFERRÁVEL" ao comportamento "IMEDIATO" e que faria sentido para o Postgres ser mais indulgente e fazer com que NÃO DEFERRÁVEL se comporte como "IMEDIATO". A ordem na qual as linhas são atualizadas em uma instrução UPDATE nem mesmo é documentada ou especificada, pelo que eu sei, então o comportamento dessa verificação de restrição no meio da instrução não é pelo menos parcialmente não especificado? Não consigo ver por que alguém iria querer esse comportamento - mas talvez haja um caso de uso razoável para ele que não tenho imaginação para ver.
Mark Amery
1
Sim, basicamente a única razão para preferir NOT DEFERRABLEé a velocidade ( veja aqui , a seção Restrições de exclusividade não adiadas , "Esteja ciente de que isso pode ser significativamente mais lento do que a verificação de exclusividade imediata" ).
Teejay
Além disso, a DEFERRABLErestrição não pode ser referenciada como chaves estrangeiras em outras tabelas ( veja aqui , seção Parâmetros , "As colunas referenciadas devem ser as colunas de uma restrição de chave primária ou única não diferível na tabela referenciada" ).
Teejay
1
Portanto, como regra geral, se realmente não importa para a lógica de negócios quando a restrição é verificada, devo usar o padrão NOT DEFERRABLEsimplesmente porque tem um desempenho melhor?
dvtan
1
Em nosso ORM agora usamos as seguintes regras: 1) se a restrição for PK, usamos NOT DEFERRABLE2) se pelo menos um FK referenciar a restrição, usamos NOT DEFERRABLEtambém 3) nos outros casos, usamos DEFERRABLE INITIALLY IMMEDIATE. Isso pode diminuir ligeiramente o desempenho dessas restrições, mas garante a compatibilidade máxima com outros DBMS que usamos (Oracle, SqlServer). PK e FK não são um problema porque nunca atualizamos seus valores (o que eu acho um bom hábito de programação para bancos de dados).
Teejay
29

Além do óbvio de ser capaz de adiar, a diferença é realmente o desempenho. Se não houvesse uma penalidade de desempenho, não haveria necessidade de ter a opção de escolher diferível ou não - todas as restrições seriam simplesmente postergáveis.

A penalidade de desempenho tem a ver com otimizações que o banco de dados pode realizar dado o conhecimento de como os dados são restritos. Por exemplo, o índice criado para apoiar uma restrição exclusiva no Oracle não pode ser um índice exclusivo se a restrição for postergável, pois a permissão temporária de duplicatas deve ser permitida. No entanto, se a restrição não for postergável, o índice pode ser exclusivo.

Ryan
fonte
7

Estou muito atrasado para a festa, mas gostaria de acrescentar que - em dezembro de 2018 - apenas dois bancos de dados que eu conheço (pode haver mais) oferecem algum nível de implementação desse recurso SQL padrão :

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 Embora o Oracle 12c aceite o NOT DEFERRABLE estado de restrição , ele realmente o ignora e o faz funcionar como DEFERRABLE INITIALLY IMMEDIATE.

Como você pode ver, o Oracle não implementa o primeiro tipo ( NOT DEFERRABLE) e é por isso que os desenvolvedores que usam o Oracle (o OP, neste caso) podem ficar confusos e considerar os primeiros dois tipos equivalentes.

Curiosamente, o Oracle e o PostgreSQL têm um tipo padrão diferente. Talvez tenha implicações de desempenho.

O Empalador
fonte
4

NOT DEFERRABLE - você não pode alterar a verificação de restrição, o oracle verifica após cada instrução (ou seja, diretamente após a instrução insert).

DEFERRABLE INITIALLY IMMEDIATE - o oracle verifica a restrição após cada instrução. MAS, você pode alterá-lo para após cada transação (ou seja, após o commit):

set constraint pk_tab1 deferred;
hajili
fonte