12 - Capítulo 8 - Integridade

61
CAPÍTULO 8 Integridade 8.1 INTRODUÇÃO O termo integridade se refere à precisão ou correção de dados no banco de dados. Como observamos no Capítulo 3, um determinado banco de dados pode estar sujeito a um número muito grande de restrições de integridade de complexidade (em geral) arbitrária. Por exemplo, no caso de fornecedores e peças, números de fornecedores poderiam ter a forma Fnnnn (onde nnnn quer dizer até quatro dígitos decimais) e ser exclusivos; valores de status poderiam estar no intervalo de 1 a 100; fornecedores de Londres poderiam ter o status 20; quantidades de remessas poderiam ser múltiplos de 50; peças vermelhas poderiam estar armazenadas em Londres etc. Assim, em geral, o SGBD precisa ser informado dessas restrições e, é claro, precisa impor as restrições de algum modo (basicamente, rejeitando qualquer atualização que possa violá-las). Por exemplo (mais uma vez em Tutorial D): CONSTRAINT FC3 ISEMPTY ( F WHERE STATUS < 1 OR STATUS > 100 (“valores de status devem estar no intervalo de 1 a 100”). Observe o nome de restrição FC3 (“restrição de fornecedores três”); a restrição será registrada no catálogo do sistema sob esse nome e ele aparecerá em qualquer mensagem de diagnóstico produzida pelo sistema em resposta a uma tentativa de violar a restrição. A própria restrição de integridade é especificada como uma expressão booleana que não deve ter valor falso. Nota: supomos a versão algébrica de Tutorial D como definitiva; em conseqüência, a expressão booleana será com freqüência — embora nem sempre — da forma IS EMPTY (...), significando que não existem dados no banco de dados que

Transcript of 12 - Capítulo 8 - Integridade

Page 1: 12 - Capítulo 8 - Integridade

CAPÍTULO 8

Integridade

8.1 INTRODUÇÃO O termo integridade se refere à precisão ou correção de dados no banco de dados. Como observamos no Capítulo 3, um determinado banco de dados pode estar sujeito a um número muito grande de restrições de integridade de complexidade (em geral) arbitrária. Por exemplo, no caso de fornecedores e peças, números de fornecedores poderiam ter a forma Fnnnn (onde nnnn quer dizer até quatro dígitos decimais) e ser exclusivos; valores de status poderiam estar no intervalo de 1 a 100; fornecedores de Londres poderiam ter o status 20; quantidades de remessas poderiam ser múltiplos de 50; peças vermelhas poderiam estar armazenadas em Londres etc. Assim, em geral, o SGBD precisa ser informado dessas restrições e, é claro, precisa impor as restrições de algum modo (basicamente, rejeitando qualquer atualização que possa violá-las). Por exemplo (mais uma vez em Tutorial D): CONSTRAINT FC3 ISEMPTY ( F WHERE STATUS < 1 OR STATUS > 100 (“valores de status devem estar no intervalo de 1 a 100”). Observe o nome de restrição FC3 (“restrição de fornecedores três”); a restrição será registrada no catálogo do sistema sob esse nome e ele aparecerá em qualquer mensagem de diagnóstico produzida pelo sistema em resposta a uma tentativa de violar a restrição. A própria restrição de integridade é especificada como uma expressão booleana que não deve ter valor falso. Nota: supomos a versão algébrica de Tutorial D como definitiva; em conseqüência, a expressão booleana será com freqüência — embora nem sempre — da forma IS EMPTY (...), significando que não existem dados no banco de dados que violem a restrição (consulte o Capítulo 6, Seção 6.9). Um análogo do cálculo para o exemplo mostrado seria semelhante a este:* CONSTRAINT FC3 FORALL FX ( FX.STATUS 1 AND FX.STATUS 100 (onde, é claro, FX é uma variável de intervalo que varia sobre fornecedores).

Page 2: 12 - Capítulo 8 - Integridade

A propósito, observamos que a expressão booleana em uma restrição de cálculo tem de ser uma FBF fechada (consulte o Capítulo 7, Seção 7.2) e com freqüência — embora nem sempre — ela terá forma FORALL.x (...). Assim, note que o exemplo especifica que todos os valores de status de fornecedores devem estar no intervalo indicado. É claro que, na prática, basta que o sistema verifique o fornecedor recém-inserido ou atualizado, e não todos os fornecedores. * Na prática, muitas vezes parece mais fácil formular restrições (especialmente as complicadas) em termos de cálculo do que em termos de álgebra. Nosso foco sobre a álgebra neste capítulo é feito por consistência com nossas discussões em outras partes do livro, mas você talvez prefira experimentar o exercício de converter alguns dos exemplos seguintes para a forma de cálculo. 219

Quando uma nova restrição é declarada, o sistema deve primeiro ter certeza de que o banco de dados a satisfaz. Se não, a nova restrição é rejeitada; do contrário, ela é aceita (isto é, gravada no catálogo) e imposta a partir desse instante. No exemplo, a imposição exige que o SGBD monitore todas as operações que fariam a inserção de um novo fornecedor ou a alteração do status de um fornecedor existente. É claro que também precisamos de um modo para eliminar restrições existentes: DROP CONSTRAINT <nome da restrição> Por exemplo: DROP CONSTRAINT FC3 Nota: como vimos, nossa preocupação é especificamente com o suporte de integridade declarativa. Infelizmente, poucos produtos atuais oferecem de modo adequado tal suporte. Embora a situação esteja melhorando lentamente nesse aspecto, ainda se pode afirmar que alguns produtos (em especial os não relacionais) enfatizam de modo muito específico a abordagem oposta — isto é, o suporte procedural, usando procedimentos armazenados ou triggers.* Porém, foi sugerido que, se o SGBD fornecesse de fato suporte declarativo, então mais de 90% da definição de um banco de dados típico consistiriam em restrições; assim, um sistema que fornecesse tal suporte aliviaria os programadores de aplicações de um trabalho considerável e os tornaria bem mais produtivos. O suporte de integridade declarativa é importante. Antes de seguirmos adiante, devemos dizer que a parte de integridade do modelo relacional talvez seja a parte que mais mudou ao longo dos anos (talvez seja mais bem dizer a que

Page 3: 12 - Capítulo 8 - Integridade

mais evoluiu em vez de mudou). Como mencionamos no Capítulo 3, a ênfase original era especificamente sobre chaves primárias e estrangeiras (“chaves”, para abreviar). Porém, gradualmente a importância — na verdade, a importância crucial! — de restrições de integridade em geral começou a ser mais bem entendida e mais apreciada; ao mesmo tempo, certas questões complicadas relativas a chaves em particular começaram a surgir. A estrutura deste capítulo reflete essa mudança de ênfase, pois ele trata de restrições de integridade em geral (até certo ponto), e depois discute as chaves — o que continua a ter maior importância pragmática — em seguida. Um esquema de classificação de restrições Seguindo a referência [3.3], classificamos as restrições de integridade em geral em quatro categorias amplas: de tipo (domínio), de atributo, de variável de relação e de banco de dados. Basicamente: • Uma restrição de tipo especifica os valores válidos de um dado tipo. Nota: em todo este capítulo, usaremos “tipo” com o significado específico de tipo escalar. Os tipos de relações também estão sujeitos a restrições de tipo, mas essas restrições são apenas uma conseqüência lógica das restrições de tipo que se aplicam aos tipos escalares em termos dos quais esses tipos de relações são definidos (em última análise). • Uma restrição de atributo especifica os valores válidos para um dado atributo. • Uma restrição de variável de relação especifica os valores válidos para uma dada variável de relação. • Uma restrição de banco de dados especifica os valores válidos para um determinado banco de dados. Os quatro casos serão discutidos em detalhes das Seções 8.2 a 8.5. * Procedimentos armazenados e triggers são procedimentos pré-compilados que podem ser invocados a partir de programas de aplicação. Os exemplos poderiam incluir os operadores definidos pelo usuário ABS, DIST, REFLECT etc. discutidos na Seção 5.2 (subseção “Definição de operadores”). Esses procedimentos podem ser considerados logicamente uma extensão ao SGBD (em um sistema cliente/servidor, eles serão com freqüência mantidos e executados no site servidor). Voltaremos a esses procedimentos na Seção 8.8, na anotação a 220

algumas referências do final do capítulo, e também no Capítulo 20.

Page 4: 12 - Capítulo 8 - Integridade

8.2 RESTRIÇÕES DE TIPO Em essência, uma restrição de tipo é (ou é logicamente equivalente a) apenas uma enumeração dos valores válidos do tipo. Aqui está um exemplo simples, a restrição de tipo para o tipo PESO: TYPE PESO POSSREP ( RATIONAL );CONSTRAINT THE PESO (PESO ) > 0.0 Adotamos uma convenção óbvia pela qual uma restrição de tipo pode fazer uso do nome de tipo aplicável para denotar um valor arbitrário do tipo em questão; assim, esse exemplo restringirá os pesos de tal modo que eles possam ser representados por um número racional maior que zero. Qualquer expressão que deva ser avaliada como um peso mas que não produza de fato um valor que satisfaça a essa restrição irá falhar. Nota: consulte o Capítulo 5 se precisar rever as especificações de POSSREP e operadores THE_. Deve ficar claro que, em última análise, o único modo pelo qual qualquer expressão pode produzir um valor do tipo PESO é através de alguma invocação de seletor PESO. Assim, o único modo pelo qual essa expressão pode violar a restrição de tipo PESO é se a invocação de seletor em questão o fizer. Desse modo, essas restrições de tipo sempre podem ser vistas, pelo menos conceitualmente, como verificadas durante a execução de alguma invocação de seletor. Em conseqüência, podemos dizer que as restrições de tipo são verificadas imediatamente e, então, que nenhuma variável de relação poderá adquirir um valor para qualquer atributo de qualquer tupla que não seja do tipo apropriado (é claro, em um sistema que admita restrições de tipo de forma apropriada!). Aqui está outro exemplo de restrição de tipo: TYPE PONTO POSSREP CARTESIAN ( X RATIONAL, Y RATIONAL );CONSTRAINT ABS ( THE_X ( PONTO ) ) 100.0 AND ABS ( THEY ( PONTO ) ) 100.0 Aqui, a verificação de tipo é feita conceitualmente durante a execução de invocações do seletor CARTESIANO. Observe o uso do operador definido pelo usuário ABS (consulte o CapítuloS, Seção 5.2). Aqui está um terceiro exemplo: TYPE ELIPSE POSSREP ( A COMPRIMENTO, B COMPRIMENTO, CTR PONTO );CONSTRAINT THE_A ( ELIPSE ) THE_B ( ELIPSE); Agora, os componentes da representação possível, especificamente A, B e CTR, representam respectivamente o comprimento do semi-eixo maior a, o comprimento de semi-eixo menor b e o centro ctr da elipse. Suponha que a variável escalar E seja declarada como do tipo ELIPSE, e que seu valor atual tenha o comprimento do semi-eixo maior igual a cinco e

Page 5: 12 - Capítulo 8 - Integridade

do semi-eixo menor igual a quatro. Agora, considere a atribuição: THE_B ( E ) : COMPRIMENTO ( 6.0 );Nota: baseamos esse exemplo em uma variável escalar e em uma atribuição escalar apenas por razões de simplicidade, mas poderíamos ter usado como base uma variável de relação e uma atribuição relacional. É claro que a atribuição mostrada falhará, mas não é a atribuição em si que está errada. Em vez disso, o erro ocorre de novo dentro de uma invocação de seletor (embora nenhuma invocação esteja visível diretamente na atribuição), porque a atribuição mostrada é de fato a abreviação para:* *Em outras palavras — e apesar do fato de não termos mencionado esse detalhe de modo explícito no Capítulo 5 — as pseudovariáveis THE_ são logicamente desnecessárias! Ou seja, qualquer atribuição a uma pseudovariável THE_ é sempre logicamente equivalente (e é de fato definida como uma abreviação para) à atribuição do resultado de uma certa invocação de seletor a uma variável comum. 221

E := ELIPSE ( THE_A ( E ), COMPRIMENTO ( 6,0 ), THE_CTR ( E É a invocação de seletor no lado direito que falha 8.3 RESTRIÇÕES DE ATRIBUTOS Uma restrição de atributo é apenas uma declaração para o efeito de que um atributo especificado seja de um tipo especificado. Por exemplo, considere mais uma vez a definição da variável de relação de fornecedores: VAR F BASE RELATION F# F#, SNOME NOME, STATUS INTEGER, CIDADE CHAR Nessa variável de relação, os valores dos atributos F#, FNOME, STATUS e CIDADE estão limitados aos tipos F#, NOME, INTEGER e CHAR, respectivamente. Em outras palavras, as restrições de atributos fazem parte da definição do atributo em questão e podem ser identificadas por meio do nome do atributo correspondente. Segue-se que uma restrição de atributo só pode ser descartada se descartarmos o próprio atributo (o que na prática significará normalmente descartar a variável de relação que a contém). Nota: em princípio, qualquer tentativa de introduzir um valor de atributo no banco de dados que não seja um valor do tipo relevante será simplesmente rejeitado. Porém, na prática, tal situação nunca deve ocorrer, pois o sistema impõe de fato as restrições de tipo descritas na seção anterior.

Page 6: 12 - Capítulo 8 - Integridade

8.4 RESTRIÇÕES DE VARIÁVEIS DE RELAÇÕES Uma restrição de variável de relação é uma restrição sobre uma variável de relação individual (expressa somente em termos da variável de relação em questão, embora possa ser arbitrariamente complexa em outros aspectos). Aqui estão alguns exemplos: CONSTRAINT FC5 ISEMPTY ( F WHERE CIDADE = Londres AND STATUS 20 (“fornecedores de Londres devem ter o status 20”). CONSTRAINT PC4 ISEMPTY ( P WHERE COR = COR ( Vermelho AND CIDADE ‘Londres (“peças vermelhas devem estar armazenadas em Londres”). CONSTRAINT FCK COUNT(F)COUNT(F{F#}) (“números de fornecedores são exclusivos” ou, de modo mais formal, “{F#} é uma chave candidata para fornecedores” — consulte a Seção 8.8). CONSTRAINT PC7 IF NOT ( IS_EMPTY ( P ) ) THEN COUNT ( P WHERE COR = COR ( Vermelho’ ) ) > O END IF ; 222

(“se existem peças, pelo menos uma delas deve ser vermelha”). A propósito, observe que esse exemplo difere de todos os outros que vimos pois operações DELETE também têm o potencial de violar a restrição. As restrições de variáveis de relações são sempre verificadas imediatamente (na verdade, como parte da execução de qualquer instrução que possa fazer com que elas sejam violadas). Portanto, qualquer instrução que tente atribuir um valor a uma dada variável de relação que viole qualquer restrição para essa variável de relação efetivamente será rejeitada. 8.5 RESTRIÇÕES DE BANCOS DE DADOS Uma restrição de banco de dados é uma restrição que relaciona entre si duas ou mais variáveis de relações distintas. Aqui estão alguns exemplos: CONSTRAINT DBC1 ISEMPTY ( ( F JOIN FP ) WHERE STATUS < 20 AND QDE > QDE ( 500 (“nenhum fornecedor com status menor que 20 pode fornecer qualquer peça em uma quantidade maior que 500”). Exercício: que operações de atualização o SGBD tem de monitorar para impor a restrição DBC1? CONSTRAINT DBC2 FP { F# } F { F# } ;

Page 7: 12 - Capítulo 8 - Integridade

(“todo número de fornecedor na variável de relação de remessa também existe na variável de relação de fornecedores”; lembre-se de que, no Capítulo 6, usamos ““ para indica “subconjunto de”). Como o atributo F# na variável de relação F constitui uma chave candidata para fornecedores, essa restrição é basicamente a restrição referencial necessária de remessas para fornecedores (isto é, {F#} na variável de relação FP é uma chave estrangeira para remessas que se refere a fornecedores). Consulte a Seção 8.8 para ver uma descrição adicional. CONSTRAINT DBC3 FP { P# } P { P# } ; (“toda peça deve ter pelo menos uma remessa”). Nota: é claro que também é verdade que toda remessa deve ter exatamente uma peça, em virtude do fato de que {P#} na variável de relação P é uma chave candidata para peças e existe uma restrição referencial de remessas para peças; não nos preocupamos em mostrar aqui essa última restrição. De novo, consulte a Seção 8.8. Esses dois últimos exemplos servem para ilustrar o ponto de que (em geral) a verificação de uma restrição de banco de dados não pode ser feita de imediato, mas deve ser adiada até o final da transação — isto é, até o instante de COMMIT (consulte o Capítulo 3 se precisar rever COMMIT). Vamos supor, ao contrário, que a verificação fosse imediata e que não existisse nenhuma peça ou remessa no momento. Então, a inserção de uma peça falhará porque ela viola a restrição DBC3; da mesma forma, a inserção de uma remessa falhará, porque ela viola a restrição DBC2.* Se uma restrição de banco de dados for violada no momento de COMMIT, é feito um ROLLBACK da transação. 8.6 A REGRA ÁUREA Nota: o material desta seção é de importância fundamental. Porém, infelizmente, ele não tem aceitação muito ampla na prática, nem sequer é bem compreendido, embora em princípio seja bastante simples. Pedimos desculpas ao leitor. *Na realidade, a referência [3.3] propõe uma forma múltipla de atribuição que permitiria a inserção de peças e remessas em uma única operação. Se tais atribuições fossem aceitas, as restrições de bancos de dados poderiam ser verificadas imediatamente. 223

No Capítulo 3, Seção 3.4, explicamos como qualquer relação dada tinha um predicado associado e como tuplas dessa relação denotavam proposições verdadeiras derivadas desse predicado. No Capítulo 5, Seção 5.3, mencionamos a Hipótese do Mundo

Page 8: 12 - Capítulo 8 - Integridade

Fechado, que diz na realidade que, se uma certa tupla não aparece em uma certa relação, somos obrigados a supor que a proposição correspondente é falsa. Embora não tenhamos enfatizado esse detalhe antes, deve ficar claro que uma variável de relação também tem um predicado; ou seja, o predicado comum a todas as relações possíveis que são valores váudos da variável de relação em questão. Por exemplo, considere a variável de relação F de fornecedores. O predicado para essa variável de relação seria semelhante a: O fornecedor com o número de fornecedor especificado (F#) tem o nome (FNOME) especificado e o valor de status especificado (STATUS), e está localizado na cidade especificada (CIDADE); além disso, não há dois fornecedores com o mesmo número de fornecedor ao mesmo tempo. Essa declaração não é exata nem completa, mas servirá para os propósitos atuais. Também deve ficar claro que, fundamentalmente, o predicado para uma dada variável de relação serve como critério para aceitação de atualizações na variável de relação — ele determina se uma certa operação TNSERT, UPDATE ou DELETE sobre essa variável de relação deve ter sucesso. Por exemplo, uma tentativa de inserir um novo fornecedor com número de fornecedor igual ao de algum fornecedor existente seguramente será rejeitado. Assim, no caso ideal, o SGBD conheceria e entenderia o predicado de toda variável de relação de modo que ele pudesse lidar corretamente com toda as tentativas possíveis de atualizar o banco de dados. Porém, é claro que essa meta é inalcançável. Por exemplo, não há nenhum meio pelo qual o SGBD possa saber o que significa um certo fornecedor estar “em” uma certa cidade. E não há nenhum meio pelo qual o SGBD possa saber a priori que o predicado para fornecedores é tal que (por exemplo) a tupla { F# F# ( Fi’ ) FNOME NOME ( Smith STATUS 20 CIDADE : Londres o satisfaz, enquanto a tupla { F# : F# ( ‘F6’ ) FNOME NOME ( ‘Smith’ STATUS : 50 CIDADE Roma’ não o satisfaz. Na verdade, se o usuário final apresentar essa última tupla para inserção, o sistema só poderá verificar se ela não viola alguma restrição de integridade conhecida. Supondo-se que não, o sistema tentará aceitar a tupla para inserção e a tratará como uma proposição

Page 9: 12 - Capítulo 8 - Integridade

verdadeira desse momento em diante. Então, repetimos: o sistema não sabe (e não pode saber) nem entende 100% o predicado de fornecedores. Porém, ele conhece uma boa aproximação para esse predicado; para sermos específicos, ele conhece as restrições de integridade que se aplicam a fornecedores. Assim, definimos o predicado de variável de relação para a variável de relação de fornecedores (ou, de modo mais geral, para qualquer variável de relação como a operação AND lógica entre todas as restrições da variáveis de relações que se aplicam a essa variável de relação. Por exemplo, o predicado de variável de relação para a variável de relação F é semelhante a este: ISEMPTY ( F WHERE STATUS < 1 OR STATUS > 100 ) ) AND ISEMPTY ( F WHERE CIDADE = ‘Londres’ AND STATUS 20 ) ) AND ( COUNT (F) = COIJNT ( F { F# 1 ) ) (É claro que, além disso, o sistema sabe que os atributos F#, FNOME, STATUS e CIDADE são do tipo F#, NOME, INTEGER e CHAR, respectivamente.) 224

Então, observe que há efetivamente dois predicados associados com qualquer variável de relação dada: o predicado informal ou externo, entendido pelos usuários mas não pelo sistema, e o predicado formal ou interno, entendido pelos usuários e peio sistema. Além disso, é claro que o predicado interno é o que indicamos pela expressão “predicado de variável de relação” e é o predicado interno que o sistema verificará sempre que houver uma tentativa de atualização da variável de relação em questão. Na verdade, de agora em diante usaremos o termo predicado (quando em conexão com alguma variável de relação) para indicar especificamente o predicado interno dessa variável de relação, impedindo declarações explícitas em contrário. Dadas as definições anteriores, podemos agora enunciar A Regra Aurea (pelo menos, a primeira versão dessa regra): Não deve ser permitida nenhuma operação de atualização que deixe qualquer variável de relação em um estado que viole seu próprio predicado. Também devemos enfatizar que “variável de relação” não significa necessariamente uma variável de relação básica — A Regra Aurea se aplica a todas as variáveis de relações, derivadas ou básicas. Voltaremos a esse ponto no próximo capítulo. Fechamos esta seção destacando que, assim como toda variável de relação tem um predicado, todo banco de dados também tem um predicado associado — o predicado de banco de dados para esse banco de dados, que definimos como a operação AND lógica

Page 10: 12 - Capítulo 8 - Integridade

de todas as restrições de bancos de dados e variáveis de relações que se aplicam a esse banco de dados. Assim, podemos agora estender A Regra Aurea: * Não deve ser permitida nenhuma operação de atualização que deixe qualquer variável de relação em um estado que viole seu próprio predicado. Da mesma forma, não deve ser permitida nenhuma transação de atualização que deixe o banco de dados em um estado que viole seu próprio predicado. 8.7 RESTRIÇÕES DE ESTADO E RESTRIÇÕES DE TRANSIÇÃO Todas as restrições discutidas neste capítulo até agora foram restrições de estado: elas estavam relacionadas com estados corretos do banco de dados. Porém, às vezes é necessário considerar também as restrições de transição — isto é, restrições sobre transições válidas de um estado correto para outro. Por exempio, em um banco de dados de pessoas, haveria uma série de restrições de transição relacionadas com mudanças no estado civil. Seriam válidas todas as transições a seguir: • Solteiro para casado. • Casado para viúvo. • Casado para divorciado. • Viúvo para casado. (etc.), enquanto estas não são: • Solteiro para viúvo. • Solteiro para divorciado. • Viúvo para divorciado. • Divorciado para viúvo. (etc.). Voltando ao banco de dados de fornecedores e peças, aqui está outro exemplo (“nenhum status de fornecedor deve jamais diminuir”): CONSTRAINT TRC1 ISEMPTY F’ { F#, STATUS } RENAME STATUS AS STATUS’ JOIN F { F#, STATUS WHERE STATUS’ > STATUS *Se forem admitidas várias atribuições (uma possibilidade mencionada em uma nota de rodapé anterior), talvez pudéssemos dei xa A Regra Aurea em sua forma original e mais simples. 225

Explicação: introduzimos a convenção de que um nome de variável de relação com apóstrofo, como F’ no exemplo, se refere a uma variável de relação como ela era antes da atualização sob consideração. A restrição do exemplo pode então ser entendida como: se (a) fizermos a junção (sobre números de fornecedores) da relação que é o valor da variável de relação F antes da atualização com a relação que é seu valor posterior, e se (b) escolhermos nessa junção as tuplas

Page 11: 12 - Capítulo 8 - Integridade

para as quais o valor de status antigo é maior que o novo, então (c) o resultado final deverá ser vazio. (Como a junção é sobre números de fornecedores, qualquer tupla no resultado da junção para a qual o valor de status antigo fosse maior que o do novo representaria um fornecedor cujo status diminuiu.) Nota: a restrição TRC1 é uma restrição de transição de variável de relação (ela se aplica apenas a uma única variável de relação, ou seja, fornecedores) e, portanto, a verificação é imediata. Em contraste, aqui está um exemplo de uma restrição de transição de banco de dados (“a quantidade total de qualquer peça dada, considerada sobre todos os fornecedores, nunca pode diminuir”): CONSTRAINT TRC2 ISEMPTY ( ( ( SUMMARIZE FP’ PER F { F# } ADD SUM ( QDE ) AS FQ’ ) JOIN ( SUMMARIZE FP PER F { F# } ADD SUM ( QDE ) AS FQ ) WHERE FQ’ > FQ ) ; A restrição TRC2 é uma restrição de transição de banco de dados (ela envolve duas variáveis de relações distintas, fornecedores e remessas); portanto, a verificação é adiada até o momento de COMMIT, e os nomes de variáveis de relações F’ e FP’ significam variáveis de relações F e FP como elas eram em BEGIN TRANSACTION. O conceito de restrições de estado e restrições de transição não tem significado para restrições de tipo ou de atributo. 8.8 CHAVES O modelo relaciona! sempre enfatizou o conceito de chaves, embora tenhamos visto que elas são na realidade apenas um caso especial — apesar de importante — de um fenômeno mais geral. Nesta seção, vamos voltar nossa atenção especificamente para as chaves. Nota: embora as idéias básicas sejam bastante simples nesse caso, infelizmente existe um fator complicador significativo: os nulos. A possibilidade de que (por exemplo) uma dada chave estrangeira possa permitir nulos complica o quadro de forma considerável. No entanto, os nulos formam por si próprios um tópico extenso e poderia ser inadequado discuti-lo em detalhes neste momento. Portanto, considerando razões pedagógicas, vamos ignorar quase totalmente os nulos nesta seção; voltaremos a discutir o impacto dos nulos sobre chaves quando discutirmos os nulos em geral, no Capítulo 18. (Na verdade, é nossa opinião que os nulos são um equívoco e nunca deveriam ter sido introduzidos, mas seria errado ignorá-los por completo em um livro desta natureza.) Chaves candidatas

Page 12: 12 - Capítulo 8 - Integridade

Seja R uma variável de relação. Por definição, o conjunto de todos os atributos de R tem a propriedade de unicidade significando que, em qualquer instante, não há duas tuplas no valor de R nesse instante que sejam duplicatas uma da outra. Na prática, ocorre com freqüência que algum subconjunto próprio do conjunto de todos os atributos de R também tenha a propriedade de unicidade; por exemplo, no caso da variável de relação de fornecedores F, o subconjunto que contém apenas o atributo F# tem essa propriedade. Esses fatos constituem a intuição que rege a definição de chave candidata: • Seja K um conjunto de atributos da variável de relação R. Então, K é uma chave candidata paraR se e somente se ela possui ambas as propriedades a seguir:’ Observe que a definição se aplica especificamente a variáveis de relações. É possível definir uma noção análoga também para 226 valores de relações [3.3] mas as variáveis de relações são o caso importante.

a. Unicidade: nenhum valor válido deR contém duas tuplas diferentes com o mesmo valor para K. b. Irredutibilidade: nenhum subconjunto próprio de K tem a propriedade de unicidade. Note que toda variável de relação tem pelo menos uma chave candidata. A propriedade de unicidade de tais chaves é auto-explicativa. Quanto à propriedade de irredutibilidade, o ponto é que, se fôssemos especificar uma “chave candidata” que não fosse irredutível, o sistema não ficaria a par do real estado de coisas, e assim não seria capaz de impor adequadamente a restrição de integridade associada. Por exemplo, suponha que fôssemos definir a combinação {F#,CIDADE} — em vez de {F#} apenas — como chave candidata para fornecedores. Então, o sistema não imporia a restrição de que números de fornecedores são “globalmente” únicos; em vez disso, imporia somente a condição mais fraca de serem os números de fornecedores “localmente” únicos dentro da cidade. Por essa razão, entre outras, exigimos que chaves candidatas não incluam atributos que sejam irrelevantes para fins de identificação única. * A propósito, a irredutibilidade no sentido mencionado antes é dita minimalidade na maior parte da literatura (inclusive em edições anteriores deste livro). Porém “minimalidade” não é realmente o mais correto, porque dizer que uma chave candidata K1 é “mínima” não significa que não se possa achar uma outra chave candidata K2 com menos componentes; é inteiramente possível que (por exemplo) K1 tenha quatro componentes e K2 apenas dois. Ficaremos com o termo “irredutível”. Em Tutorial D, usaremos a sintaxe:

Page 13: 12 - Capítulo 8 - Integridade

KEY { <lista_com_vírgulas de nomes de atributos> dentro de uma definição de variável de relação, a fim de especificar uma chave candidata para a variável de relação. Aqui estão alguns exemplos: • VAR F BASE RELATION { F# F#, SNOME NOME, STATUS INTEGER, CIDADE CHAR KEY { F# } ; Nota: em capítulos anteriores, mostramos essa definição com uma cláusula PRIMARY KEY, e não com uma cláusula KEY. Consulte a subseção “Chaves primárias e chaves alternativas” mais adiante nesta seção, para ver uma discussão e uma explicação adicional. • VAR FP BASE RELATION { F# F#, P# P#, QDE QDE } KEY { F#, P# 1 . . . ; Esse exemplo mostra uma variável de relação com uma chave candidata composta (isto é, uma chave candidata envolvendo mais de um atributo). Uma chave candidata simples é aquela que não é composta. • VAR ELEMENTO BASE RELATION { NOME NOME, SÍMBOLO CHAR, ATÔMICO# INTEGER } KEY { NOME KEY { SÍMBOLO KEY { ATÔMICO# } ; Esse exemplo mostra uma variável de relação com diversas chaves candidatas (simples) distintas. * Outra boa razão para se exigir que chaves candidatas sejam irredutíveis tem a ver com chaves estrangeiras correspondentes. Qualquer chave estrangeira que referenciasse uma chave candidata “redutível” (se isso fosse possível) seria também “redutível” e a variável de relação que a contém estaria então quase certamente violando os princípios de normalização avançada (consulte o Capítulo 11). 227

• VAR CASAMENTO BASE RELATION { MARIDO NOME, ESPOSA NOME, DATA /* do casamento / DATE /* supondo-se que não há poliandria nem poliginia, e / que nenhum marido e nenhuma esposa se casam um com / / o outro mais de uma vez ... *1

Page 14: 12 - Capítulo 8 - Integridade

KEY { MARIDO, DATA KEY { DATA, ESPOSA KEY { ESPOSA, MARIDO Esse exemplo mostra uma variável de relação com diversas chaves candidatas compostas (e sobrepostas) distintas. É claro que, como destacamos na Seção 8.4, uma definição de chave candidata é na realidade apenas uma abreviação para uma certa restrição de variável de relação. A abreviação é útil porque o conceito de chave candidata é muito importante de um ponto de vista pragmático. Para sermos específicos, as chaves candidatas fornecem o mecanismo básico de endereçamento no nível de tupla no modelo relacional — isso significa que o único modo garantido pelo sistema de se apontar com precisão alguma tupla específica é pelo valor de alguma chave candidata. Por exemplo, a expressão: F WHERE F# = F# ( ‘F3’ ) oferece a garantia de fornecer no máximo uma tupla (mais precisamente, ela produz uma relação que contém no máximo uma tupla). Ao contrário, a expressão F WHERE CIDADE = ‘Paris’ fornecerá um número imprevisível de tuplas, em geral. Segue-se que chaves candidatas são tão fundamentais para a operação bem-sucedida de um sistema relacional quanto os endereços da memória principal para a operação bem-sucedida da máquina subjacente. Em conseqüência: 1. “Variáveis de relações” que não tenham uma chave candidata — isto é, “variáveis de relações” que permitam tuplas duplicadas — certamente exibirão um comportamento estranho e anômalo em algumas circunstâncias. 2. Um sistema que não tenha conhecimento de chaves candidatas certamente exibirá em algumas ocasiões um comportamento que não é “verdadeiramente relacional”, mesmo se as variáveis de relações com que ele lida sejam de fato variáveis de relações verdadeiras e não permitem tuplas duplicadas. O comportamento mencionado antes como “estranho e anômalo” e “não verdadeiramente relaciona 1” tem a ver com questões como atualização de visões e otimização. Consulte os Capítulos 9 e 17, respectivamente. Alguns pontos finais para fecharmos esta subseção: • Um superconjunto de chave candidata é uma superchave. Por exemplo, o conjunto de atributos {F#,CIDADE} é uma superchave para a variável de relação F. Uma superchave tem a propriedade de unicidade, mas não necessariamente a propriedade de irredutibilidade (é claro que uma chave candidata é um caso especial de uma superchave). • Se FK é uma superchave para a variável de relação R e A é um atributo de R, então a dependência funcional FK —> A é

Page 15: 12 - Capítulo 8 - Integridade

necessariamente verdadeira em R (esse conceito importante será discutido em profundidade no Capítulo 10). De fato, podemos definir uma superchave como um subconjunto FK dos atributos de R, tal que a dependência funcional FK — A é verdadeira para todos os atributos A de R. 228

• Por fim, observe que a noção lógica de chave candidata não deve ser confundida com a noção física de um “índice exclusivo” (embora essa última seja usada com muita freqüência para implementar a primeira). Em outras palavras, não há nenhuma implicação que tenha de existir um índice (ou, de fato, qualquer outro caminho de acesso físico especial) em uma chave candidata. Na prática, provavelmente haverá algum caminho de acesso especial, mas o fato dele existir ou não está além do escopo do modelo relaciona!. Chaves primárias e chaves alternativas Como vimos, é possível que uma dada variável de relação tenha mais de uma chave candidata. Em tal caso, o modelo relacional historicamente tem exigido — pelo menos no caso de variáveis de relações básicas — que exatamente uma dessas chaves candidatas seja escolhida como a chave primária, e as outras sejam então chamadas chaves alternativas. Por exemplo, em ELEMENTOS, poderíamos escolher o {SIMBOLO} como chave primária; {NOME} e {ATOMICO#} seriam então chaves alternativas. E, no caso em que só existe uma chave candidata, o modelo relaciona! (mais uma vez) historicamente tem exigido que essa chave candidata seja designada como a chave primária para a variável de relação em questão. Portanto, toda variável de relação básica sempre tem uma chave primária. Ora, escolher uma chave candidata (nos casos em que existe uma escolha) como chave primária poderia ser um boa idéia em muitos casos — até mesmo na maioria dos casos — mas não pode ser justificada em todos os casos, inequivocamente. Argumentos detalhados em apoio a essa posição são dados na referência [8.13]; aqui vamos apenas observar um caso em que a escolha de qual chave candidata será primária é essencialmente arbitrária (para citar Codd [8.8], “a base norma! para fazer a escolha] é a simplicidade, mas esse aspecto está fora do escopo do modelo relacional”). Em nossos próprios exemplos, definiremos às vezes uma chave primária e em outras vezes não (é claro que sempre definiremos pelo menos uma chave candidata). Chaves estrangeiras Informalmente, uma chave estrangeira é um conjunto de atributos de uma variável de relação R2 cujos valores devem

Page 16: 12 - Capítulo 8 - Integridade

obrigatoriamente corresponder a valores de alguma chave candidata de alguma variável de relação Ri. Por exemplo, considere o conjunto de atributos {F#} da variável de relação FP (um conjunto que contém apenas um atributo, é claro). Deve ficar claro que um dado valor para {F#} deve poder aparecer na variável de relação FP somente se esse mesmo valor também aparecer como um valor da única chave candidata {F#} para a variável de relação F (não podemos ter uma remessa para um fornecedor que não existe). Da mesma forma, um dado valor para o conjunto de atributos {P#} deve poder aparecer na variável de relação FP somente se o mesmo valor também aparecer como valor da única chave candidata {P#} para a variável de relação P (também não podemos ter uma remessa de uma peça que não existe). Esses exemplos servem como motivação desta definição: • Seja R2 uma variável de relação. Então, uma chave estrangeira em R2 é um conjunto de atributos de R2, digamos FK, tal que: a. Existe uma variável de relação Ri (Ri e R2 não necessariamente distintas) com uma chave candidata CK, e b. Para sempre, cada valor de FK no valor atua! de R2 é idêntico ao valor de CK em alguma tupla no valor atual de RI.

Surgem alguns pontos importantes: 1. A definição exige que todo valor de uma dada chave estrangeira tem de aparecer como valor de uma chave candidata correspondente (a qual é em geral, mas nem sempre, especificamente uma chave primária). Porém, observe que a recíproca não é um exigência; isto é, a chave candidata correspondente a uma dada chave estrangeira poderia conter um valor que não aparece no momento como um valor dessa chave estrangeira. No caso de fornecedores e peças, por exemplo (amostras de valo- 229

res como na Figura 3.8), o fornecedor número F5 aparece na variável de relação F, mas não na variável de relação FP, porque o fornecedor F5 não está fornecendo peças no momento. 2. Uma chave estrangeira é simples ou composta, conforme a chave candidata que corresponde a ela seja simples ou composta. 3. Cada atributo de uma dada chave estrangeira deve ter o mesmo nome e tipo que o componente correspondente da chave candidata associada. 4. Terminologia: um valor de chave estrangeira representa uma referência à tupla que contém o valor da chave candidata associada (a tupla referenciada). O problema de garantir que o banco de dados não inclui quaisquer valores inválidos de

Page 17: 12 - Capítulo 8 - Integridade

chave estrangeira é então conhecido como o problema de integridade referencial. A restrição de que valores de uma dada chave estrangeira devem se ajustar a valores da chave candidata correspondente chama-se restrição referencial. Dizemos que a variável de relação que contém a chave estrangeira é a variável de relação referente e que a variável de relação que contém a chave candidata correspondente é a variável de relação referida. 5. Diagramas referenciais: considere mais uma vez fornecedores e peças. Podemos representar as restrições referenciais que existem nesse banco de dados por meio do seguinte diagrama referencial: F — FP —* P Cada seta significa que há uma chave estrangeira na variável de relação da qual a seta emerge, que se refere especificamente a alguma chave candidata na variável de relação para a qual a seta aponta. Nota: por simplicidade e clareza, às vezes é uma boa idéia identificar cada seta em um diagrama referencial com o(s) nome(s) do(s) atributo(s) que constitui(em) a chave estrangeira relevante.* Por exemplo: F# P# F — FP —* P Porém, neste livro mostraremos essas etiquetas (labels) apenas quando omiti-las puder levar a confusão ou ambigüidade. 6. É claro que uma dada variável de relação pode ser uma relação referida e referente, como acontece com a relação R2 no diagrama a seguir: R3 —* R2 —* Ri E conveniente introduzir a expressão caminho referencial. Sejam as relações Rn, R(n-i), ... R2, Ri tais que exista uma restrição referencial de Rn para R(n-i), uma restrição referencial de R(n-i) para R(n-2), ... e um restrição referencial de R2 para Ri: Rn —* R(n-1) —* R(n-2) —*... —*R2 —*Ri Então, a cadeia de setas de Rn até Ri representa um caminho referencial de Rn a Ri. 7. Observe que as variáveis de relações Ri e R2 na definição de chaves estrangeiras não são necessa ria- mente distintas. Isto é, uma variável de relação poderia incluir uma chave estrangeira cujos valores devem combinar com os valores de alguma chave candidata na mesma variável de relação. Por exemplo, considere a definição de variável de relação a seguir (explicaremos a sintaxe em breve mas, de qualquer modo, ela deve ser auto-explicativa): VAR EMP BASE RELATION EMP# EMP#, . . ., GER EMP# EMP#, . .

Page 18: 12 - Capítulo 8 - Integridade

PRIMARY KEY { EMP# } FOREIGN KEY { RENAME GER EMP# AS EMP# } REFERENCES EMP De forma alternativa (e talvez de preferência), poderíamos nomear as chaves estrangeiras, e depois usar esses nomes para iden230 tificar as setas.

Aqui, o atributo GER_EMP# representa o número de empregado do gerente do empregado identificado por EMP#. Por exemplo, a tupla para o empregado E4 pode incluir um valor GER_EMP# igual a E3, que representa uma referência à tupla de EMP para o empregado E3. (Observe a necessidade de renomear um atributo de chave estrangeira nesse exemplo, a fim de obedecer às exigências do parágrafo 3 anterior.) Tal variável de relação é às vezes dita auto-referente. Exercício: crie algumas amostras de dados para esse exemplo. 8. Variáveis de relações auto-referentes como EMP na verdade representam um caso particular de uma situação mais geral — ou seja, podem existir ciclos referenciais. As variáveis de relações Rn, R(n-i), R(n-2), ..., R2, Ri formam um ciclo referencial se Rn incluir uma chave estrangeira fazendo referência a R(n-1), R(n-1) incluir uma chave estrangeira fazendo referência a R(n-2), ... e assim por diante, e finalmente Ri incluir uma chave estrangeira fazendo referência de novo a Rn. De modo mais sucinto, existe um ciclo referencial se existe um caminho referencial de alguma variável de relação Rn para ela mesma: Rn — R(n-1) —* R(n-2) —+... —*R2 —* Ri —* Rn 9. Associações de chaves estrangeiras para chaves candidatas são ditas às vezes serem a “cola” que mantém unido o banco de dados. Outro modo de dizer isso é que essas combinações representam certos relacionamentos entre tuplas. Porém, observe com cuidado que nem todos esses relacionamentos são representados por chaves desse modo. Por exemplo, existe um relacionamento (“co-localização”) entre fornecedores e peças, representado pelos atributos CIDADE das relações F e P; um dado fornecedor e uma certa peça são co-localizados se estão localizados na mesma cidade. Contudo, esse relacionamento não é representado por chaves. 10. Historicamente, o conceito de chave estrangeira tem sido definido apenas para variáveis de relações básicas, um fato que por si só levanta algumas questões (veja a discussão do Princípio da permutabilidade no Capítulo 9, Seção 9.2). Não impomos tal restrição aqui; no entanto, vamos limitar nossas discussões somente às variáveis de relações básicas (onde fizer diferença), por razões de simplicidade. 11. Originalmente, o modelo relacional exigia que as chaves estrangeiras referenciassem, de forma muito específica,

Page 19: 12 - Capítulo 8 - Integridade

chaves primárias, não apenas chaves candidatas (por exemplo, veja de novo a referência [8.8]). Rejeitamos essa limitação como desnecessária e indesejável em geral, embora ela possa com freqüência constituir boa disciplina na prática [8.131. Normalmente, seguiremos essa disciplina em nossos próprios exemplos. 12. Juntamente com o conceito de chave estrangeira, o modelo relacional inclui as seguinte regra (a regra de integridade referencial): • Integridade referencial: o banco de dados não deve conter quaisquer valores de chaves estrangeiras não-associados. Aqui, a expressão “valor de chave estrangeira não correspondente” significa apenas um valor de chave estrangeira em alguma variável de relação referente para a qual não existe um valor associado da chave candidata relevante na variável de relação referenciada relevante. Em outras palavras, a restrição diz apenas: se B faz referência a A, então A tem de existir. Então, aqui está a sintaxe para a definição de uma chave estrangeira: FOREIGN KEY { <lista com vírgulas de itens > } REFERENCES <nome de variável de relação> *A regra de integridade referencial pode ser considerada uma metarrestrição: ela irriplica que qualquer banco de dados deve estar sujeito a certas restrições dc integridade específicas ao banco de dados em questão que, juntas, garantem que a regra não será violada por esse banco de dados. Observamos de passagem que o modelo relacional é considerado como incluindo outra “metarrestrição”, a regra de integridade de entidades; essa última regra está relacionada com nulos e, portanto, adiaremos sua dis cussã para o Capítulo 18. 231

Essa cláusula aparece dentro de uma definição de variável de relação referente. Observe que: • Cada <item> é um <nome de atributo> da variável de relação referente ou uma expressão da forma: RENAME <nome de atributo> AS <nome de atributo> (veja a variável de relação auto-referente EMP anterior como exemplo do caso de RENAME). • O <nome de variável de relação> identifica a variável de relação referenciada. Já foram dados exemplos em muitos pontos anteriores no livro (por exemplo, veja a Figura 3.9 no Capítulo 3). Nota: é claro que, como mencionamos na Seção 8.5, uma definição de chave estrangeira é na realidade apenas uma abreviação para uma certa restrição de banco de dados (ou uma certa restrição de

Page 20: 12 - Capítulo 8 - Integridade

variável de relação, no caso de uma variável de relação auto-referente) — a menos que a definição da chave estrangeira seja estendida para incluir certas “ações referenciais”, e nesse caso ela se torna mais que apenas uma restrição de integridade. Veja as duas subseções imediatamente a seguir. Ações referenciais Considere a seguinte instrução: DELETE F WHERE F# = F# ( ‘Fi’ ) ; Suponha que essa operação DELETE faz exatamente o que seu nome diz — isto é, elimina a tupla de fornecedor correspondente ao fornecedor Fi, nem mais nem menos. Suponha também que (a) o banco de dados inclui algumas remessas para o fornecedor Fi, como na Figura 3.8, e (b) a aplicação não continua a eliminar essas remessas. Então, quando o sistema verificar a restrição referencial de remessas para fornecedores, encontrará uma violação e ocorrerá um erro. Nota: como a restrição referencial no caso é uma restrição de banco de dados, a verificação será feita no momento de COMMIT — pelo menos conceitualmente (o sistema pode verificar de fato a restrição logo que DELETE for executada, mas uma violação nesse momento não será necessariamente um erro; significará apenas que o sistema terá de fazer a verificação de novo no momento de COMMIT). Contudo, existe uma abordagem alternativa, uma que pode ser preferível em alguns casos e que faz o sistema executar uma ação de compensação apropriada que irá garantir que o resultado geral ainda atenderá à restrição. No exemplo, a ação de compensação óbvia seria o sistema eliminar as remessas para o fornecedor Fi “de forma automática”. Podemos conseguir esse efeito estendendo a definição de chave estrangeira, assim: VAR FP BASE RELATION { FOREIGN KEY { F# } REFERENCES F ON DELETE CASCADE A especificação ON DELETE CASCADE define uma regra DELETE para essa chave estrangeira em particular, e a especificação CASCADE é a ação referencial para essa regra DELETE. O significa dessas especificações é que uma operação DELETE sobre a variável de relação de fornecedores deve “propagar” a eliminação até as tuplas correspondentes na variável de relação de remessas. Outra ação referencial comum é RESTRICT (não relacionada com o operador de restrição da álgebra relacional). No caso, RESTRICT significaria que operações DELETE são “restritas” ao caso em que não há nenhuma remessa correspondente (elas são rejeitadas caso contrário). Omitir uma ação referencial para

Page 21: 12 - Capítulo 8 - Integridade

uma determinada chave estrangeira é equivalente a especificar a “ação” NO ACTION, que significa exatamente o que seu nome diz — a operação DELETE é executada exatamente como foi solicitada, nem mais nem menos. (E claro que se NO ACTION for especificada no caso e um fornecedor que tenha remessas correspondentes for eliminado, teremos em seguida uma violação de integrida232 de referencial.) Surgem pontos importantes:

1. DELETE não é a única operação para a qual as ações referenciais fazem sentido. Por exemplo, o que aconteceria se tentássemos atualizar o número de fornecedor correspondente a um fornecedor para o qual existisse pelo menos uma remessa associada? E claro que precisaríamos também de uma regra UPDATE, além de uma regra DELETE. Em geral, há as mesmas possibilidades para UPDATE que existem para DELETE: • CASCADE — UPDATE atua em cascata para atualizar a chave estrangeira também nessas remessas associadas. • RESTRICT — a operação UPDATE é restrita ao caso em que não há nenhuma remessa associada (do contrário, ela é rejeitada).

• NO ACTION — UPDATE é executada exatamente como foi solicitada. 2. É claro que CASCADE, RESTRICT e NO ACTION não são as únicas ações referenciais possíveis — são apenas as que se exigem normalmente na prática. Porém, em princípio, poderia haver um número arbitrário de respostas possíveis a, por exemplo, uma tentativa de eliminar um determinado fornecedor. Vejamos: • As informações poderiam ser gravadas em algum banco de dados de arquivo morto. • As remessas para o fornecedor em questão poderiam ser transferidas para algum outro fornecedor, e assim por diante. Nunca será viável fornecer a sintaxe declarativa para todas as respostas imagináveis. Assim, em geral, deve ser possível especificar uma ação referencial da forma “CALL proc (...)“, onde proc é um procedimento definido pelo usuário. Nota: a execução desse procedimento deve ser considerada parte da execução da transação que provocou a verificação de integridade. Além disso, essa verificação de integridade deve ser executada novamente após esse procedimento ser executado (o procedimento não deve obviamente deixar o banco de dados em um estado que viole a restrição). 3. Sejam R2 e Ri, respectivamente uma variável de relação referente e a variável de relação referida correspondente: R2 —* Ri Suponha que a regra DELETE aplicável especifica CASCADE.

Page 22: 12 - Capítulo 8 - Integridade

então, uma operação DELETE sobre uma dada tupla de Ri implicará uma DELETE em certas tuplas da variável de relação R2 (em geral). Agora, suponha que a variável de relação R2 seja por sua vez referenciada por alguma outra variável de relação R3: R3 —* R2 —* Ri Então, o efeito da operação DELETE implícita sobre as tuplas de R2 é definido exatamente como se fosse feita uma tentativa de eliminar essas tuplas de forma direta; isto é, ele depende da regra DELETE especificada para a restrição referencial de R3 para R2. Se essa DELETE implícita falhar (devido à regra DELETE de R3 para R2 ou por qualquer outra razão), então a operação inteira falhará, e o banco de dados permanecerá inalterado. E assim por diante, recursivamente, até qualquer número de níveis. Aplicam-se comentários análogos também à regra CAS CADE UPDATE, mutatis mutandis, se a chave estrangeira na variável de relação R2 tiver quaisquer atributos em comum com a chave candidata da variável de relação referenciada pela chave estrangeira em R3. 4. Concluímos do que foi dito que, de um ponto de vista lógico, as atualizações de bancos de dados são sempre atômicas (tudo ou nada), mesmo se nos bastidores elas envolvem diversas atualizações sobre diversas variáveis de relações, em virtude de, por exemplo, uma ação referencial CASCADE. 233

Procedimentos triggers Como você já deve ter percebido (e como de fato o comentário da subseção anterior a respeito de procedimentos definidos pelo usuário deve ter sugerido), todo o conceito de ações referenciais nos leva até além das restrições de integridade, em direção aos procedimentos triggers. Um procedimento trigger (normalmente chamado apenas trigger na literatura) é um procedimento invocado “automaticamente” na ocorrência de algum evento especificado ou de uma condição de trigger. A condição de trigger é em geral a execução de alguma operação de atualização do banco de dados, mas poderia ser por exemplo a ocorrência de uma exceção especificada (em particular, a violação de uma determinação restrição de integridade) ou a passagem de um certo intervalo de tempo. As ações referenciais CASCADE fornecem um exemplo simples de procedimento trigger (especificado de forma declarativa, como podemos observar!). í Em geral, procedimentos triggers se aplicam a uma variedade muito mais ampla de problemas que apenas a questão da

Page 23: 12 - Capítulo 8 - Integridade

integridade que é o tópico deste capítulo (uma boa lista dessas aplicações pode ser encontrada na referência [8.1]). Porém, eles representam sozinhos um assunto extenso, um tópico que está além do escopo deste capítulo (consulte a referência [8.22] para ver uma discussão mais completa). Gostaríamos de dizer que, embora os procedimentos triggers certamente sejam úteis para muitos fins, eles normalmente não são uma boa abordagem para o problema específico de integridade de bancos de dados, por razões óbvias (abordagens declarativas, se forem possíveis, sempre serão preferíveis). Nota: essas observações não significam a sugestão de que as ações referenciais são uma idéia ruim. Embora seja verdade que as ações referenciais provocam a chamada de certos procedimentos, pelo menos elas são (como já observamos) especificadas de forma declarativa. 8.9 RECURSOS DE SQL O esquema de classificação de restrições de integridade da SQL é muito diferente daqueles descritos nas Seções 8.1 a 8.5. Antes de tudo, ele classifica as restrições em três amplas categorias, assim: • Restrições de domínios • Restrições de tabelas básicas • Restrições gerais (“assertivas”) Entretanto, as “restrições de domínios” não são iguais às nossas restrições de tipo, “restrições de tabelas básicas” não são iguais às nossas restrições de variáveis de relações, e “assertivas” não são iguais às nossas restrições de bancos de dados. Na verdade: • A SQL não admite realmente restrições de tipo de forma alguma (porque, é claro, ela na realidade não admite tipos de forma alguma, exceto alguns tipos embutidos). • As restrições de “domínios” de SQL são uma forma generalizada indesejável de nossas restrições de atributos (lembre-se de que os domínios no estilo de SQL não são na realidade domínios de modo algum no sentido relaciona!). • As restrições de tabelas básicas e assertivas de SQL (que são na verdade intercambiáveis) dificilmente seriam equivalentes ao conjunto de nossas restrições de variáveis de relações e de bancos de dados. Observamos também que a SQL não fornece nenhum suporte para restrições de transição, nem admite no momento procedimentos triggers, embora uma parte desse suporte esteja incluída na SQL3 (consulte o Apêndice B). Restrições de domínios Uma restrição de domínio no estilo de SQL é uma restrição que

Page 24: 12 - Capítulo 8 - Integridade

se aplica a cada coluna definida no domf234 nio em questão. Aqui está um exemplo:

CREATE DOMAIN COR CHAR(6) DEFAULT ‘???‘ CONSTRAINT VALIDCOLORS CHECK ( VALUE IN ‘Vermelho’, Amarelo’, ‘Azul , ‘Verde’, ‘??? Suponha que CREATE TABLE para a tabela básica P seja semelhante a: CREATE TABLE P ( ... , COR COR, Então, como default, se o usuário inserir uma linha de peça e não fornecer um valor de COR para essa linha, o valor “???“ será inserido nessa posição. Como alternativa, se o usuário fornecer um valor de COR, mas ele não fizer parte do conjunto válido, a operação falhará e o sistema produzirá um diagnóstico apropriado que mencionará a restrição VALID_COLORS pelo nome. Vimos na Seção 8.2 que uma restrição de domínio é — ou melhor, deve ser — conceitualmente nada além de uma enumeração dos valores que formam esse domínio, e o exemplo de VALID COLORS de fato sustenta essa definição. Porém, em geral, a SQL permite que uma restrição de domínio envolva uma expressão booleana de complexidade arbitrária; portanto, por exemplo, os valores válidos para algum domínio D podem depender dos valores presentes no momento em alguma tabela T. Você deve meditar sobre algumas implicações dessa permissividade não justificada. Restrições de tabelas básicas Uma restrição de tabela básica de SQL é qualquer das seguintes: • Uma definição de chave candidata • Uma definição de chave estrangeira • Uma definição de “restrição de verificação” Discutiremos cada caso em detalhes a seguir. Nota: qualquer dessas definições pode ser precedida opcionalmente pela sentença “CONSTRAINT <nome da restrição>”, fornecendo assim um nome para a nova restrição (o mesmo vale para restrições de domínios, como vimos antes no exemplo de VALID_COLORS). Para abreviar, vamos ignorar essa opção. Chaves candidatas: uma definição de chave candidata tem a forma: UNIQUE ( <lista com vírgulas de nomes de colunas> ou a forma: PRIMARY KEY ( <lista com vírgulas de nomes de colunas> Uma <lista_com_vírgulas de nomes de colunas> não deve ser vazia em nenhum dos casos. Uma determinada tabela básica pode ter no máximo uma especificação de PRIMARY KEY, mas qualquer

Page 25: 12 - Capítulo 8 - Integridade

número de especificações UNIQUE. No caso de PRIMARY KEY, cada coluna especificada é, além disso, considerada NOT NULL, mesmo que NOT NULL não seja especificada de forma explícita (veja a discussão sobre restrições de verificação a seguir). Chaves estrangeiras: uma definição de chave estrangeira obtém a forma: FOREIGN KEY ( <lista com vírgulas de nomes de colunas> REFERENCES <nome de tabela básica> [ ( <lista_com_vírgulas de nomes de colunas> ) 1 [ ON DELETE <ação referencial> ] ON UPDATE <ação referencial> 1 235

onde <ação referencial> é NO ACTION (o default) ou CASCADE ou SET DEFAULT ou SET NULL. Adiaremos a discussão de SET DEFAULT e SET NULL para o Capítulo 18. A segunda <lista_com_vírgulas de nomes de colunas> é necessária se a chave estrangeira faz referência a uma chave candidata que não é uma chave primária. Nota: a correspondência de chave estrangeira para chave candidata é feita com base não nos nomes de colunas, mas na posição de colunas (da esquerda para a direita) dentro das listas_com_vírgulas. Restrições de verificação: uma “definição de restrição de verificação” toma a forma: CHECK ( <expressão condicional> Uma tentativa de criar uma linha r dentro da tabela básica T é considerada uma violação a uma restrição de verificação para T se a expressão condicional especificada dentro dessa restrição tiver o valor falso para r. Nota: as expressões condicionais são o análogo em SQL daquilo que chamamos em outros lugares expressões booleanas. Elas serão explicadas em detalhes no Apêndice A. Observe em particular que (nesse contexto), a expressão condicional pode ser arbitrariamente complexa — ela não está limitada explicitamente a uma condição que se refira apenas a T, mas pode se referir a qualquer outro item no banco de dados. Mais uma vez, procure meditar sobre algumas implicações dessa permissividade não justificada. Então, aqui está um exemplo de CREATE TABLE envolvendo restrições de tabelas básicas dos três tipos: CREATE TABLE FP ( F# F# NOT NULL, P# P# NOT NULL, QDE QDE NOT NULL, PRIMARY KEY ( F#, P# ), FOREIGN KEY ( F# ) REFERENCES F ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY ( P# ) REFERENCES 1’

Page 26: 12 - Capítulo 8 - Integridade

ON DELETE CASCADE ON UPDATE CASCADE. CHECK ( QDE > O AND QDE < 5001 Estamos supondo aqui que (a) os domínios F#, P# e QDE já foram definidos e que (b) F# e P# foram definidos de modo explícito como chaves primárias para as tabelas F e P, respectivamente. Além disso, também fazemos uso deliberado da abreviação pela qual uma restrição de verificação da forma: CHECK ( <nome de coluna> IS NOT NULL pode ser substituída por uma simples especificação NOT NULL na definição da coluna em questão. No exemplo, substituímos assim três restrições de verificação um pouco incômodas por três especificações simples de NOT NULL. Fechamos esta subseção com um comentário sobre um ponto de certa forma estranho, ou seja: uma restrição de tabela básica de SQL é sempre considerada satisfeita se a tabela básica está vazia — mesmo que a restrição seja da forma “esta tabela não deve estar vazia”! Assertivas No restante desta seção, vamos concentrar nossa atenção no terceiro caso, as restrições gerais ou “assertivas”. As restrições gerais são definidas por meio da sintaxe de CREATE ASSERTION: CREATE ASSERTION <nome de restrição> CHECK ( <expressão condicional> )

236

E aqui está a sintaxe de DROP ASSERTION: DROP ASSERTION <nome de restrição> Observe que, diferente de todas as outras formas do operador DROP de SQL discutidas neste livro (DROP DOMAIN, DROP TABLE, DROP VIEW), DROP ASSERTION não oferece uma opção RESTRICT versus CASCADE. Aqui estão alguns exemplos de CREATE ASSERTION: 1. Todo fornecedor tem status pelo menos cinco: CREATE ASSERTION 1C13 CHECK ( ( SELECT MIN ( F.STATiJS ) FROM F ) > 4 ) 2. Toda peça tem um peso positivo: CREATE ASSERTION 1C18 CHECK ( NOT EXISTS ( SELECT * FROM P WHERE NOT ( P.PESO > 0.0 ) ) ) 3. Todas as peças vermelhas devem estar armazenadas em Londres: CREATE ASSERTION 1C99 CHECK ( NOT EXISTS ( SELECT * FROM P WHERE P.COR = ‘Vermelho’

Page 27: 12 - Capítulo 8 - Integridade

AND P.CIDADE <> ‘Londres’ 4. Nenhuma remessa tem um peso total (peso da peça vezes quantidade da remessa) maior que 20.000: CREATE ASSERTION 1C46 CHECK ( NOT EXISTS ( SELECT * FROM P, FP WHERE P.P# FP.P# AND ( P.PESO * FP.QDE ) > 20000.0 5. Nenhum fornecedor com status menor que 20 pode fornecer qualquer peça em quantidade maior que 500: CREATE ASSERTION 1C95 CHECK ( NOT EXISTS ( SELECT * FROM F, FP WHERE F.STATUS < 20 AND F.F# = FP.F# AND FP.QDE > 500 Verificação postergada O esquema de classificação de restrições de integridade de SQL também difere do nosso quanto à questão de quando a verificação é feita. Em nosso esquema, as restrições de bancos de dados são verificadas no instante de COMMIT, outras são verificadas “imediatamente”. Em contraste, em SQL, as restrições podem ser definidas como DEFERRABLE ou NOT DEFERRABLE; se uma dada restrição é DEFERRABLE, ela pode ainda ser definida como INITIALLY DEFERRED ou INITIALLY IMMEDIATE, o que define seu estado no início de cada transação. As restrições NOT DEFERRABLE são sempre verificadas imediatamente, mas as restrições DEFERRABLE podem ser ativadas e desativadas dinamicamente por meio da instrução: SET CONSTRAINTS <lista com vírgulas de nomes de restrições> <opção> onde <opção> é IMMEDIATE ou DEFERRED. Aqui está um exemplo: SET CONSTRAINTS 1C46, 1C95 DEFERRED ; 237

As restrições DEFERRABLE são verificadas apenas quando se encontram no estado IMMEDIATE. A definição de uma restrição DEFERRABLE no estado IMMEDIATE faz essa restrição ser verificada de imediato, é claro; se a verificação falhar, SET IMMEDIATE falhará. O COMMTT força uma SET IMMEDIATE para todas as restrições DEFERRABLE; se qualquer verificação de integridade falhar, então a transação será retomada. RESUMO Neste capítulo, discutimos o conceito crucial de integridade. O problema da integridade é o de garantir que os dados no banco de dados são precisos ou corretos (e, é claro, estamos

Page 28: 12 - Capítulo 8 - Integridade

interessados em soluções declarativas para esse problema). Na verdade, como você deve ter percebido, nesse contexto “integridade” significa semântica: são as restrições de integridade (em particular, os predicados de variáveis de relações e bancos de dados — ver a seguir) que representam o significado dos dados. E esse é o motivo por que, como afirmamos na Seção 8.6, a integridade tem importância crucial. Dividimos as restrições de integridade em quatro categorias: • Uma restrição de tipo especifica os valores válidos para um determinado tipo (ou domínio), e é verificada durante invocações do seletor correspondente. • Uma restrição de atributo especifica os valores válidos para um determinado atributo, e nunca deve ser violada. • Uma restrição de variável de relação especifica os valores válidos para uma determinada variável de relação, e é verificada quando essa variável de relação é atualizada. • Uma restrição de banco de dados especifica os valores válidos para um determinado banco de dados, e é verificada no instante de COMMIT. A operação lógica AND de todas as restrições de variáveis de relações para uma determinada variável de relação é o predicado de variável de relação (interno) para essa variável de relação. O predicado de variável de relação é o significado da variável de relação entendido pelo sistema e é o critério para aceitação de atualizações sobre essa variável de relação. A Regra Aurea estabelece que nenhuma operação de atualização terá jamais permissão para deixar qualquer variável de relação em um estado que viole seu próprio predicado. Por sua vez, o banco de dados global é assunto para um predicado de banco de dados e nenhuma transação terá permissão para deixar o banco de dados em um estado que viole esse predicado. Em seguida, esboçamos rapidamente a idéia básica de restrições de transição (as outras restrições são restrições de estado). Então, passamos a discutir os casos especiais pragmaticamente importantes de chaves candidatas, primárias, alternativas e estrangeiras. As chaves candidatas satisfazem às propriedades de uni- cidade e irredutibilidade, e toda variável de relação tem pelo menos uma (sem exceções!). A restrição de que os valores de uma determinada chave estrangeira devem corresponder aos valores da chave candidata correspondente é uma restrição referencial; exploramos diversas implicações da idéia de integridade referencial, inclusive em particular a noção de ações referenciais (especialmente CASCADE). Essa última discussão nos levou a uma breve incursão pelo objetivo dos procedimentos triggers.

Page 29: 12 - Capítulo 8 - Integridade

Concluímos nossas discussões com um exame dos aspectos relevantes de SQL. A SQL admite restrições de “domínios”, restrições de tabelas básicas e “assertivas” (restrições gerais), e seu suporte para restrições de tabelas básicas inclui o caso especial do suporte para chaves. EXERCÍCIOS 8.1 Usando a sintaxe introduzida nas Seções 8.2 a 8.5, escrever restrições de integridade para o banco de dados de fornecedores, peças e projetos como a seguir: a. As únicas cidades válidas são Londres, Paris, Roma, Atenas, Oslo, Estocolmo, Madri e Amsterdã. 238

h. Os únicos números de fornecedores válidos são aqueles que podem ser representados por um string de caracteres de pelo menos dois caracteres, dos quais o primeiro é uma letra “F” e os restantes denotam um inteiro decimal no intervalo de 0 a 9999. c. Todas as peças vermelhas devem pesar menos de 50 libras. d. Dois projetos não podem estar localizados na mesma cidade.

e. No máximo um fornecedor pode estar localizado em Atenas em qualquer instante. f. Nenhuma remessa pode ter uma quantidade maior que o dobro da média de todas as quantidades de remessas. g. O fornecedor de status mais alto não deve estar localizado na mesma cidade que o fornecedor de status mais baixo. h. Todo projeto deve estar localizado em uma cidade na qual exista pelo menos um fornecedor. i. Todo projeto deve estar localizado em uma cidade na qual exista pelo menos um fornecedor desse projeto. j. Deve existir pelo menos uma peça vermelha. k. O status de fornecedor médio deve ser maior que 18. 1. Todo fornecedor de Londres deve fornecer a peça P2. m. Pelo menos uma peça vermelha deve pesar menos que 50 libras. n. Fornecedores em Londres devem fornecer mais tipos diferentes de peças que tornecedores em Paris. o. Fornecedores em Londres devem fornecer mais peças no total que fornecedores em Paris. p. Nenhuma quantidade de remessa pode ser reduzida (em uma única atualização) a menos de metade de seu valor atual. q. Fornecedores em Atenas só podem mudar para Londres ou Paris, e fornecedores em Londres só podem mudar para Paris. 8.2 Para cada uma de suas respostas ao Exercício 8.1, estabeleça se a restrição é uma restrição de variável de relação ou de banco de dados.

Page 30: 12 - Capítulo 8 - Integridade

8.3 Para cada uma de suas respostas ao Exercício 8.1, estabeleça as operações que poderiam fazer a restrição aplicável ser violada. 8.4 Sejam CHAR(5) e CHAR(3) strings de caracteres de comprimento cinco e três caracteres, respectivamente. Quantos tipos existem aqui — um ou dois? 8.5 Sejam A e B duas variáveis de relações. Declare a(s) chave(s) candidata(s) para cada um dos itens a seguir: a. A WHERE b. A {...} c. A TIMES 8 d. A UNION 8 e. A INTERSECT B f. A MINUS 8 g. A JOIN 8 h. EA XTEND A ADD exp AS Z i. 5 UMMARIZE A PER 8 ADD exp AS Z j. A SEMIJOIN B k. A SEMIMINUS B Em cada caso, suponha que A e B atendem às exigências para a operação em questão (por exemplo, elas são do mesmo tipo, no caso de UNION). 8.6 Seja R uma variável de relação de grau n. Qual é o número máximo de chaves candidatas que R pode possuir? 239

8.7 Seja R uma variável de relação cujos únicos valores válidos são as relações especiais (e muito importantes) de grau O DEE e DUM. Quais chaves candidatas R possui? 8.8 O texto do capítulo discutiu as regras de chaves estrangeiras DELETE e UPDATE, mas não mencionou nenhuma “regra INSERT” de chave estrangeira. Por que não? 8.9 Usando os valores de dados de fornecedores, peças e projetos da Figura 4.5, diga qual será o efeito de cada uma das seguintes operações: a. UPDATE projeto J7, definindo CIDADE como Nova York. b. UPDATE peça P5, definindo P# como P4. c. UPDATE fornecedor F5, definindo F# como F8, se a ação referencial aplicável for RESTRICT. d. DELETE fornecedor F3, se a ação referencial aplicável for CASCADE. e. DELETE peça P2, se ação referencial aplicável for RESTRICT. f. DELETE projeto J4, se ação referencial aplicável for CASCADE. g. UPDATE remessa Fi-Pi-Ji, definindo F# como F2. h. UPDATE remessa F5-P5-J5, definindo J# como J7.

Page 31: 12 - Capítulo 8 - Integridade

i. UPDATE remessa F5-P5-J5, definindo J# como J8. j. INSERT remessa F5-P6-J7. k. INSERT remessa F4-P7-J6. 1. INSERT remessa F1-P2-jjj (onde jjj representa um número de projeto padrão). 810 Um banco de dados de ensino contém informações sobre um esquema de treinamento educacional interno de uma empresa. Para cada curso de treinamento, o banco de dados contém detalhes de todos os cursos que são pré-requisitos para esse curso e todas as ofertas desse curso; e para cada oferta contém detalhes de todos os professores e todos as matrículas de alunos para essa oferta, O banco de dados também contém informações sobre empregados. As variáveis de relações são as seguintes, em um esboço: CURSO { CURSO#, TÍTULO PREREQ { SUPCURSO#, SUB CURSO# } OFERTA { CURSO#, OFER#, DATAOFER, LOCAL PROFESSOR { CURSO#, OFER#, EMP# MATRÍCULA { CURSO#, OFER#, EMP#, GRADE EMPREGADO { EMP#, ENOME, CARGO O significado da variável de relação PREREQ é que o curso superior (SUI’_CURSO#) tem o curso subordinado (SUB_CURSO#) como pré-requisito imediato; as outras variáveis de relações devem ser auto-explicativas. Trace um diagrama referencial adequado para esse banco de dados. Forneça também a definição do banco de dados correspondente (isto é, escreva um conjunto apropriado de definições de tipos e variáveis de relações). 8.11 As duas variáveis de relações a seguir representam um banco de dados contendo informações sobre departamentos e empregados: DEPTO { DEPTO# ,.., GEREMP#, ... } EMP { EMP#,..., DEPTO#, ... } Cada departamento tem um gerente (GER_EMP#); cada empregado tem um departamento (DEPTO#). Novamente, trace um diagrama referencial e escreva uma definição de banco de dados adequada para esse banco de dados. 8.12 As duas variáveis de relações a seguir representam um banco de dados contendo informações sobre empregados e programadores: EMP ( EMP#, ... CARGO, ... } PGMR { EMP#, ..., LING, ... } Todo programador é um empregado, mas a recíproca não é verdadeira. Mais uma vez, trace um diagrama 240 referencial e escreva uma definição de banco de dados adequada.

Page 32: 12 - Capítulo 8 - Integridade

8.13 Uma questão que não discutimos no texto do capítulo foi a questão do que deve acontecer se o usuário tentar descartar alguma variável de relação ou algum tipo, e alguma restrição existente fizer referência a essa variável de relação ou a esse tipo. O que deve acontecer em tal situação? 8.14 Forneça soluções em SQL para o Exercício 8.1. 8.15 Compare o suporte para integridade de SQL com o mecanismo de integridade descrito no texto deste capítulo. REFERÊNCIAS E BIBLIOGRAFIA 8.1 Alexander Aiken, Joseph M. Hellerstein e Jennifer Widom: “Static Analysis Techniques for Predicting the Behavior of Active Database Rules”, ACM TODS 20, Número 1 (março de 1995). Esse artigo continua o trabalho das referências [8.2] e [8.5] sobre “sistemas de bancos de dados especialistas” (chamados aqui de sistemas de bancos de dados ativos). Em particular, descreve o sistema de regras do protótipo Starburst da IBM (consulte as referências [17.50], [25.14], [25.17] e [25.21 e 25.22], e também a referência [8.22]. 8.2 Elena Baralis e Jennifer Widom: “An Algebraic Approach to Rule Analysis in Expert Database Systema”, Proc. ZOth Int. Conf. on Very Large Data Bases, Santiago, Chile (setembro de 1994). De acordo com esse artigo, um “sistema de banco de dados especialista” é um sistema de banco de dados que admite “regras de condição/ação” (em nossa terminologia, a condição desse acaso é uma condição trigger, e a ação é o procedimento trigger correspondente). Um problema com tais sistemas é que seu comportamento é inerentemente difícil de prever ou entender. Esse artigo apresenta métodos para determinar antes do tempo de execução se um certo conjunto de regras possui as propriedades de terminação e confluência. A terminação significa que o processamento de regras tem a garantia de não continuar para sempre. Confluência significa que o estado final do banco de dados é independente da ordem em que as regras são executadas. 8.3 Philip A. Bernstein, Barbara T. Blaustein e Edmund M. Clarke: “Fast Maintenance of Semantic Integrity Assertions Using Redundant Aggregate Data”, Proc. 6th Int. Conf. on Very Large Data Bases, Montreal, Canadá (outubro de 1980). Apresenta um método eficiente para impor restrições de integridade de um certo tipo especial. Um exemplo é “todo valor no conjunto A deve ser menor que todo valor no conjunto B”. A técnica de imposição é baseada na observação de que (por exemplo) a restrição dada é logicamente equivalente à restrição “o valor máximo emA deve ser menor que o valor mínimo em B”. Reconhecendo essa classe de restrição e

Page 33: 12 - Capítulo 8 - Integridade

mantendo de forma automática os valores máximo e mínimo relevantes em variáveis ocultas, o sistema pode reduzir o número de comparações envolvidas na imposição da restrição sobre uma atualização de algo na ordem da cardinalidade de A ou B (dependendo do conjunto ao qual a atualização se aplica) a um — é claro, ao custo de ser obrigado a manter as variáveis ocultas. 8.4 0. Peter Buneman e Erik K. Clemons: “Efficiently Monitoring Relational Databases”, ACM TODS 4, Número 3 (setembro de 1979). Esse artigo se preocupa com a implementação eficiente de procedimentos armazenados (aqui chamados triggers) — em particular, com o problema de decidir quando a condição de trigger será satisfeita, sem necessariamente avaliar essa condição. Ele apresenta um método (um algoritmo de anulação) para detectar atualizações que talvez não possam satisfazer a uma determinada condição de trigger; ele também discute uma técnica para reduzir a sobrecarga de processamento no caso em que o algoritmo de anulação falha, avaliando a condição de trigger para algum subconjunto pequeno (um filtro) do conjunto total de tuplas relevantes. 8.5 Stefano Ceri e Jennifer Widom: “Deriving Production Rules for Constraint Maintenance”, Proc. l6th Int. Conf. on Very Large Data Bases, Brisbane, Austrália (agosto de 1990). Descreve uma linguagem baseada em SQL para definir restrições e oferece um algoritmo para identificar todas as operações que poderiam violar uma dada restrição. (Um esboço preliminar desse algoritmo foi apresentado antes na referência [8.11]. A existência desse algoritmo significa que não há necessidade de informar explicitamente ao SGBD quando uma restrição precisa ser verificada e, é claro, o esquema descrito no texto deste capítulo não oferece nenhum meio para que o usuário possa fazê-lo.) O artigo tam bé examina questões de otimização e correção. 241

8.6 Stefano Ceri, Piero Fraternali, Stefano Paraboschi e Letizia Tanca: “Automatic Generation of Production Rules for Integrity Maintenance”, ACM TODS 19, 3 (setembro de 1994). Esse artigo, que se baseia no trabalho da referência [8.5], introduz a possibilidade de reparação automática dos danos feitos por uma violação de restrição. AS restrições são compiladas em regras de produção com os seguintes componentes: 1. Uma lista de operações que podem violar a restrição. 2. Uma expressão booleana que terá o valor verdadeiro se a restrição for violada (basicamente apenas a negação da restrição original).

Page 34: 12 - Capítulo 8 - Integridade

3. Um procedimento de reparação em SQL O artigo também inclui uma boa pesquisa sobre trabalhos relacionados. 8.7 Roberta Cochrane, Hamid Pirahesh e Nelson Mattos: “Integrating Triggers and Declarative Constraints in SQL Database Systems”, Proc. 22nd Int. Conf. on Very Large Data Bases, Mumbai (Bombaim), India (setembro de 1996). Citando o artigo: “A semântica da interação de triggers e restrições declarativas deve ser definida com cuidado para evitar a execução inconsistente e fornecer aos usuários um modelo amplo para compreensão dessas interações. Este artigo define tal modelo”. O modelo em questão foi implementado em DB2 e é “aceito como o modelo para o padrão emergente de SQL (SQL3)” (consulte o Apêndice B). 8.8 E. F. Codd: “Domains, Keys and Referential Integrity in Relational Databases”, InfoDB 3, Número 1 (primavera de 1988). Uma discussão dos conceitos de domínio, chave primária e chave estrangeira. O artigo evidentemente tem autoridade, pois Codd foi o inventor de todos esses conceitos; porém, na opinião deste autor, ele ainda deixa muitas questões não resolvidas ou não explicadas. A propósito, o artigo oferece o seguinte argumento em favor da disciplina de exigir que uma única chave candidata seja escolhida como chave primária: “deixar de apoiar essa disciplina é algo como tentar usar um computador com o esquema de endereçamento ... que altere o radical sempre que uma espécie particular de evento ocorrer (por exemplo, encontrar um endereço que seja um número primo)”. Contudo, se aceitarmos esse argumento, por que não levá-lo à sua conclusão lógica e usar em esquema de endereçamento idêntico para tudo? Não é muito estranho ter de “endereçar” fornecedores por números de fornecedores e peças por números de peças? — sem mencionar as remessas, que envolvem “endereços” que são compostos. (Na verdade, há muito que dizer em favor dessa idéia de um esquema de endereçamento globalmente uniforme. Consulte a discussão sobre substitutos na anotação à referência [13.16] no Capítulo 13.) 8.9 C. J. Date: “Referential Integrity”, Proc. 7th Int. Conf. on Very Large Data Bases, Cannes, França (setembro de 1981). Republicado em forma revisada em Relational Database: Selected Writings. Reading, Mass.: Addison-Wesley (1986). O artigo que introduziu as ações referenciais (principalmente CASCADE e RESTRICT), discutidas na Seção 8.8 deste capítulo. A principal diferença entre a versão original do artigo (VLDB 1981) e a versão revista é que a versão original, seguindo a referência [13.6], permitia que uma determinada chave estrangeira fizesse referência a qualquer número de variáveis

Page 35: 12 - Capítulo 8 - Integridade

de relações, enquanto — por razões explicadas em detalhes na referência [8.10] — a versão revista recuava dessa posição excessivamente geral. 8.10 C. J. Date: “Referential Integrity and Foreign Keys” (em duas partes), em Relational Database Writings 1985—1989. Reading, Mass.: Addison-Wesley (1990). A Parte 1 desse artigo discute a história do conceito de integridade referencial e oferece um conjunto preferido de definições básicas (com justificativa). A Parte II fornece mais argumentos em favor dessas definições preferidas e dá algumas recomendações práticas específicas; em particular, discute problemas causados por (a) superposição de chaves estrangeiras (b) valores compostos de chaves estrangeiras que são parcialmente nulos, e (c) caminhos referenciais contíguos (isto é, diferentes caminhos referenciais que têm o mesmo ponto de partida e o mesmo ponto final). Nota: certas posições nesse artigo são ligeiramente (mas não muito seriamente) minadas pelos argumentos da referência [8.13]. 8.11 C. J. Date: “A Contribution to the Study of Database Integrity”, em Relational Database Writings 242 1985—1989. Reading, Mass.: Addison-Wesley (1990).

Para citar do resumo: “Esse artigo tenta impor alguma estrutura sobre o problema [da integridade] (a) propondo um esquema de classificação para restrições de integridade, (b) usando esse esquema para esclarecer os principais conceitos básicos da integridade de dados, (c) esboçando uma abordagem para uma linguagem concreta de formulação de restrições de integridade e (d) identificando algumas áreas específicas para pesquisa adicional.” Partes deste capítulo se baseiam nesse artigo anterior, mas o esquema de classificação propriamente dito deve ser considerado como superado pela versão revisada descrita nas Seções de 8.2 a 8.5 deste capítulo. 8.12 C. J. Date: “Integrity”, Capítulo 11 de C. J. Date e Colin J. White, A Guide to DB2 (4 edição) [4.20]. Reading, Mass.: Addison-Wesley (1993). O produto DB2 da IBM fornece suporte declarativo para chave primária e chave estrangeira (na verdade, foi um dos primeiros produtos a fazê-lo, senão o primeiro). Porém, como explica esta referência, esse suporte sofre de certas restrições de implementação, cujo objetivo geral é garantir um comportamento previsível. Damos aqui um exemplo simples. Suponha que a variável de relação R contenha somente duas tupIas, com valores de chave primária 1 e 2 respectivamente, e considere a solicitação de atualização “Duplicar todo valor de chave primária em R”. O resultado correto é que as tuplas

Page 36: 12 - Capítulo 8 - Integridade

devem agora ter os valores de chave primária 2 e 4, respectivamente. Se o sistema atualizar o “2” primeiro (substituindo-o por “4’) e depois atualizar o “1” (substituindo-o por “2”) a solicitação terá sucesso. Se, por outro lado, o sistema atualizar — ou melhor, tentar atualizar o “1” primeiro (substituindo-o por “2”), incorrerá em uma violação da unicidade, e a solicitação falhará (o banco de dados permanecerá inalterado). Em outras palavras, o resultado da solicitação será imprevisível. Para evitar essa imprevisibilidade, o DB2 simplesmente impede situações nas quais ela poderia ocorrer. Porém, infelizmente, algumas das restrições resultantes são bastante severas [8.17]. Observe que, como sugere o exemplo anterior, o DB2 em geral efetua uma “verificação durante a execução” — isto é, aplica verificações de integridade a cada tupla individual quando atualiza essa tupla. Essa verificação durante a execução é logicamente incorreta (veja a discussão sobre operações de atualização no final da Seção 5.4, no Capítulo 5); ela é feita por razões de desempenho. 8.13 C. J. Date: “The Primacy of Primary Keys: An Investigation”, em Relational Database Writings 1991—1994. Reading, Mass.: Addison-Wesley (1995). Apresenta argumentos para apoiar a posição de que às vezes não é boa idéia tornar uma chave candidata “mais igual que outras”. 8.14 M. M. Hammer e S. K. Sarin: “Efficient Monitoring of Database Assertions”, Proc. 1978 ACM SIGMOD lnt. Conf. on Management of Data, Austin, Texas (maio/junho de 1978). É esboçado um algoritmo para gerar procedimentos de verificação de integridade mais eficiente que o método óbvio da “força bruta” de simplesmente avaliar restrições após ter sido executada uma atualização. As verificações são incorporadas ao código objeto da aplicação em tempo de compilação. Em alguns casos, é possível detectar que nenhuma verificação é necessária durante a execução. Mesmo quando elas são necessárias, com freqüência é possível reduzir de modo significativo o número de acessos ao banco de dados de diversas maneiras. 8.15 Bruce M. Horowitz: “A Run-Time Execution Model for Referential Integrity Maintenance”, Proc. 8th IEEE Int. Conf. on Data Engineering, Phoenix, Arizona (fevereiro de 1992). E bem sabido que certas combinações de 1. Estruturas referenciais (isto é, coleções de variável de relações relacionadas entre si por meio de restrições rcferenciais) 2. Regras DELETE e UPDATE de chaves estrangeiras 3. Valores de dados reais no banco de dados

Page 37: 12 - Capítulo 8 - Integridade

podem juntos levar a certas situações de conflito e potencialmente podem causar comportamento imprevisível por parte da implementação (por exemplo, consulte a referência [8.101 para uma explicação adicional). Existem três abordagens amplas para se lidar com esse problema: a. Deixá-lo para o usuário. b. Fazer o sistema detectar e rejeitar tentativas de definir estruturas que possam levar potencialmente a conflitos em tempo de execução. c. Fazer o sistema detectar e rejeitar conflitos reais durante a execução. 243

A opção a. não vem ao caso e a opção b. tende a ser excessivamente cautelosa [8.12, 8.17]; portanto, Horowitz propõe a opção c. O artigo oferece um conjunto de regras para essas ações em tempo de execução e demonstra sua correção. Porém, observe que a questão da sobrecarga de desempenho dessa verificação durante a execução não é considerada. Horowitz foi membro ativo do comitê que definiu a SQL/92, e as porções sobre integridade referencial desse padrão implicam efetivamente que as propostas desse artigo devem ser suportadas. 8.16 Victor M. Markowitz: “Referential Integrity Revisited: An Object-Oriented Perspective”, Proc. l6th Int. Conf. on Very Large Data Bases, Brisbane, Austrália (agosto de 1990). A “perspectiva orientada a objetos” do título desse artigo reflete a declaração da posição inicial do autor de que “a integridade referencial é a base da representação relaciona! de estruturas orientadas a objetos”. Porém, o artigo não trata na realidade de orientação a objetos. Em vez disso, ele apresenta um algoritmo que, partindo de uma diagrama de entidades/relacionamento (consulte o Capítulo 13) irá gerar uma definição de banco de dados relaciona! na qual certas situações problemáticas identificadas na referência [8.10] (por exemplo, superposição de chaves) tem a garantia de não ocorrer. O artigo também discute três produtos comerciais (DB2, SYBASE e Ingres, como eram em 1990) sob o ponto de vista da integridade referencial. O DB2, que oferece suporte declarativo, se mostra bastante restritivo; o Sybase e o Ingres, que oferecem suporte procedural (por meio de “gatilhos” e “regras”, respectivamente) são apresentados como menos restritivos que o DB2, mas incômodos e difíceis de usar (embora o suporte do Ingres seja considerado “tecnicamente superior” ao do Sybase). 8.17 Victor M. Markowitz: “Safe Referential Integrity Structures in Relational Databases”, Proc. l7th Int. Conf. on

Page 38: 12 - Capítulo 8 - Integridade

Very Large Data Bases, Barcelona, Espanha (setembro de 1991).

Propõe duas “condições de segurança” que garantem que certas situações problemáticas discutidas (por exemplo) nas referências [8.10] e [8.15] não podem ocorrer. O artigo também considera o que está envolvido para satisfazer essas condições em DB2, SYBASE e Ingres (novamente, como eles eram em 1990). Quanto ao DB2, é mostrado que algumas restrições de implementação impostas no interesse da segurança [8.12] são logicamente desnecessárias, enquanto outras são inadequadas (isto é, o DB2 ainda assim permite que ocorram certas situações inseguras). Quanto a SYBASE e Ingres, afirma-se que o suporte procedural encontrado nesses produtos não fornece meios para a detecção de especificações referenciais inseguras — ou mesmo incorretas! 8.18 Ronald G. Ross: The Business Ride Book: Classi[ying, Defining, and Modeling Rules (versão 3.0). Boston, Mass.: Database Research Group (1994). Consulte a anotação à referência [8.19]. 8.19 Ronald G. Ross: Business Ride Concepts. Houston, Texas: Business Rule Solutions mc. (1998). Uma grande quantidade de suporte para regras de negócio vem sacudindo o mundo comercial nos últimos anos; alguns especialistas da indústria começaram a sugerir que elas poderiam ser uma base melhor para projeto e elaboração de bancos de dados e de aplicações de bancos de dados (melhor, vale dizer, que técnicas mais estabelecidas como a “modelagem de entidades/relacionamentos”, “modelagem de objetos”, “modelagem semântica” e outras). E nós concordamos com isso, porque as “regras de negócio” não são essencialmente nada além de um modo mais amistoso (isto é, menos acadêmico e menos formal) de se falar sobre predicados, proposições e todos os outros aspectos da integridade discutidos neste capítulo. Ross é um dos primeiros defensores da abordagem de regras de negócio e seus livros são recomendados ao estudioso. A referência [8.18] é exaustiva, enquanto a referência [8.19] é um breve tutorial. 8.20 M. R. Stonebraker e E. Wong: “Access Control in a Relational Data Base Management System by Query Modification”, Proc. ACM National Conf. (1974). O protótipo University Ingres [7.11] foi o pioneiro em uma interessante abordagem para restrições de integridade (e também restrições de segurança — consulte o Capítulo 16), baseada em modificação de solicitação. As restrições de integridade foram definidas por meio da instrução DEFINE INTEGRITY — aqui está a sintaxe: DEFINE INTEGRITY ON <nome de variável de relação> IS

Page 39: 12 - Capítulo 8 - Integridade

<expressão booleana> Por exemplo: 244 DEFINE INTEGRITY ON F IS F.STATIJS > O

Suponha que o usuário U tente a seguinte operação REPLACE de QUEL:

REPLACE F ( STATUS = F.STATUS - 10 WHERE F.CIDADE = “Londres” Então, o Ingres modifica automaticamente a REPLACE para: REPLACE F ( STATUS = F.STATUS - 10 WHERE F.CIDADE = “Londres” AND ( F.STATUS — 10 ) > O E, é claro, essa operação modificada não terá a possibilidade de violar a restrição de integridade. Uma desvantagem dessa abordagem é que nem todas as restrições podem ser impostas dessa maneira simples; na verdade, QUEL. só admitia restrições nas quais a expressão booleana era uma condição de restrição simples. Porém, mesmo esse suporte limitado representava mais do que aquilo que se encontrava na maioria dos sistemas da época. 8.21 A. Walker e S. C. Salveter: “Automatic Modification of Transactions to Preserve Data Base Integrity Without Undoing Updates”, Universidade Estadual de Nova York, Stony Brook, N.Y.: Technical Report 8 1/026 (junho de 1981). Descreve uma técnica para modificação automática de qualquer “template de transação” (isto é, código-fonte de transação) em um template correspondente — seguro, no sentido de que nenhuma instância de transação que obedeça a esse template modificado terá possibilidade de violar qualquer restrição de integridade declarada. O método funciona acrescentando consultas e testes ao template original para assegurar antes de qualquer atualização ser feita que nenhuma restrição será violada. Durante a execução, se qualquer desses testes falhar, a transação será rejeitada e uma mensagem de erro será gerada. 8.22 Jennifer Widom e Stef ano Ceri (editores): Active Database Systems: Triggers and Rules forAdvanced Data- base Processing. San Francisco, Calif.: Morgan Kaufmann (1996). Um compêndio útil de artigos de pesquisa e tutoriais sobre “sistemas de bancos de dados ativos” (isto é, sistemas de bancos de dados que executam automaticamente ações especificadas em resposta a determinados eventos — em outras palavras, sistemas de bancos de dados com procedimentos triggers). São incluídas descrições de vários sistemas protótipos, inclusive o Starburst da IBM Research (veja as referências [17.50] [25.14], [25.17] e [25.21 e 25.22]) e o

Page 40: 12 - Capítulo 8 - Integridade

Postgres, da Universidade da Califórnia em Berkeley (veja as referências [25.26], [25.30] e [25.32]). O livro também resume os aspectos relevantes de SQ14192, de (uma primeira versão de) SQL3 e de certos produtos comerciais (Oracle, Informix e Ingres entre eles). Está incluída também uma extensa bibliografia. RESPOSTAS A EXERCÍCIOS SELECIONADOS 8.1 a. TYPE CIDADE POSSREP ( CHAR CONSTRAINT THE CIDADE ( CIDADE ) = ‘Londres’ OR THE CIDADE ( CIDADE ) = ‘Paris’ OR THE CIDADE ( CIDADE ) = ‘Roma’ OR THE CIDADE ( CIDADE ) ‘Atenas’ OR TRE CIDADE ( CIDADE ) = ‘Oslo’ OR THE CIDADE ( CIDADE ) = ‘Estocolmo’ OR THE CIDADE ( CIDADE ) = Madri’ OR THE CIDADE ( CIDADE ) = ‘Amsterdã’ Uma abreviação óbvia seria: a. TYPE CIDADE POSSREP ( CI-IAR CONSTRAINT THE CIDADE ( CIDADE ) IN { ‘Londres’ , ‘Paris’ ‘Roma’ , ‘Atenas’ ‘Oslo’ , ‘Estocolmo’ ‘Madri’ , ‘Amsterdã’ } ;

245

b. TYPE F# POSSREP ( CHAR ) CONSTRAINT SUBSTR ( THE_F# ( F# 1, 1) = ‘F’ AND CASTAS INTEGER ( SUBSTR ( THEF# ( F# ), 2 ) O AND CASTAS INTEGER ( SUBSTR ( THEF# ( F# ), 2 ) 9999 Supomos aqui que o operador de substring SUBSTR e o operador de conversão explícita CAST_AS INTEGER estão ambos disponíveis. c. CONSTRAINT C ISEMPTY ( P WHERE PESO PESO ( 50.0 d. CONSTRAINT D COUNT ( J ) = COUNT ( J { CIDADE e. CONSTRAINT E COUNT ( F WHERE CIDADE = ‘Atenas’ ) 1 f. CONSTRAINT F ISEMPTY ( ( EXTEND FPJ ADD 2 * AVO ( FPJ, QDE ) AS X ) WHERE QDE > X ) ; g. CONSTRAINT G ISEMPTY ( ( F WHERE STATUS = MIN ( F { STATUS } ) ) JOIN F WHERE STATUS = MAX ( F { STATUS } ) Na realidade, os termos “fornecedor de status mais alto” e “fornecedor de status mais baixo” não estão bem definidos, pois os valores de status não são únicos. Interpretamos a exigência como se Fx e Fy fossem fornecedores quaisquer com

Page 41: 12 - Capítulo 8 - Integridade

“status mais alto” e “status mais baixo”, respectivamente; então, Fx e Fy teriam de ser não co-localizados. Observe que a restrição será necessariamente violada se os valores de status “mais alto” e “mais baixo” forem iguais! — em particular, ela será violada se existir apenas um fornecedor.

h. CONSTRAINT H ISEMPTY ( J { CIDADE } MINUS F { CIDADE i. CONSTRAINT 1 ISEMPTY ( J WHERE NOT ( TUPLE { CIDADE CIDADE } IN J { J# } JOIN FPJ JOIN F ) { CIDADE } j. CONSTRAINT J NOT ( IS_EMPTY P WHERE COR = COR ( ‘Vermelho’ ) Essa restrição será violada se não existir absolutamente nenhuma peça. Uma formulação melhor seria: CONSTRAINT J ISEMPTY ( P ) OR NOT ( ISEMPTY P WHERE COR = COR ( Vermelho ) k. CONSTRAINT K IF NOT ( ISEMPTY ( F ) THEN AVO ( F, STATUS ) > 18 END IF O teste IF é para evitar o erro que ocorreria em caso contrário se o sistema tentasse verificar a restrição quando não existisse nenhum fornecedor. 1. CONSTRAINT L ISEMPTY F WHERE CIDADE ‘Londres’ ) { F# } MINUS FPJ WHERE P# = P# (‘P2’) ) { F#} m. CONSTRAINT M ISEMPTY ( P ) OR NOT ISEMPTY ( P WHERE PESO < PESO ( 50.0 ) n. CONSTRAINT N COUNT ( ( ( F WHERE CIDADE = ‘Londres’ ) JOIN FPJ ) { P# ) ) > COUNT ( ( ( F WHERE CIDADE = ‘Paris’ ) JOIN FPJ ) { P# } o. CONSTRAINT O SUM ( ( ( F WHERE CIDADE = Londres’) JOIN FPJ ), QDE ) > SUM ( ( ( F WHERE CIDADE = ‘Paris’ ) JOIN FPJ ), QDE ) p. CONSTRAINT P ISEMPTY ( ( FPJ JOIN ( FPJ’ RENAME QDE AS QDE ) ) WHERE QDE > 0,5 * QDE’ ) ;

246

q. CONSTRAINT Q ISEMPTY ( F JOIN ( F WHERE CIDADE = Atenas’ ) ) WHERE CIDADE Atenas’ AND CIDADE Londres’ AND CIDADE =‘Paris’ AND ISEMPTY ( F JOIN ( F WHERE CIDADE ‘Londres’ ) ) WHERE CIDADE ‘Londres AND CIDADE =Paris Como um exercício complementar, você poderia tentar formular

Page 42: 12 - Capítulo 8 - Integridade

as restrições anteriores em um estilo de cálculo no lugar do estilo algébrico. 8.2 As duas primeiras são restrições de tipo, é claro. Das outras, as restrições C, D, R, F, G, J, K, M, P e Q são restrições de variáveis de relações; as restantes são restrições de bancos de dados. 8.3 a. Invocação do seletor CIDADE. b. Invocação do seletor F#. c. INSERT sobre P, UPDATE sobre PESO de peça. d. INSERT sobre J, UPDATE sobre CIDADE de projeto. e. INSERT sobre F, UPDATE sobre CIDADE de fornecedor. f. INSERT ou DELETE sobre FPJ, UPDATE sobre QDE de remessa. g. INSERT ou DELETE sobre F, UPDATE sobre STATUS de fornecedor. h. INSERT sobre J, DELETE sobre F, UPDATE sobre CIDADE de fornecedor ou projeto. i. INSERT sobre J, DELETE sobre FPJ, UPDATE sobre fornecedor ou projeto. j. INSERT ou DELETE sobre P, UPDATE sobre CIDADE de peça. k. INSERT ou DELETE sobre F, UPDATE sobre STATUS de fornecedor. 1. INSERT sobre F, DELETE sobre FPJ, UPDATE sobre CIDADE de fornecedor ou sobre F# ou P# de remessa. m. INSERT ou DELETE sobre P, UPDATE sobre PESO de peça. n. INSERT ou DELETE sobre F ou FPJ, UPDATE sobre CIDADE de fornecedor ou sobre F# ou P# de remessa. o. INSERT ou DELETE sobre F ou FPJ, UPDATE sobre CIDADE de fornecedor ou sobre F# ou P# ou QDE de remessa. p. UPDATE sobre QDE de remessa. q. UPDATE sobre CIDADE de fornecedor. 8.4 Um. As especificações “(5)” e “(3)” devem ser vistas melhor como restrições de integridade. Como observamos na referência [3.31, uma conseqüência desejável desse enfoque é que, se as variáveis X eY são definidas como, digamos, CHAR(5) e CHAR(3), respectivamente, então as comparações entre X e Y são válidas — isto é, elas não violam a exigência de que os comparandos devem ser do mesmo tipo. 8.5 Oferecemos a lista a seguir como um “primeiro esboço” do conjunto de respostas (mas observe a nota ao final). a. Qualquer restrição de A herda todas as chaves candidatas de A. b. Se a projeção incluir qualquer chave candidata K de A, então K será uma chave candidata para a projeção. Do contrário, a única chave candidata será a combinação de todos os atributos da projeção (em geral). c. toda combinação K de uma chave candidata KA de A e uma

Page 43: 12 - Capítulo 8 - Integridade

chave candidata KB de B é uma chave candidata para o produto A TIMES B. d. A única chave candidata para a união A UNION B é a combinação de todos os atributos (em geral). e. Fica como exercício (a interseção não é uma primitiva). f. Toda chave candidata de A é uma chave candidata para a diferença A MINUS B. 247

g. Deixamos o caso geral como exercício (a junção natural não é uma primitiva. Porém, observamos que no caso especial em que o atributo de junção em A é uma chave candidata de A, toda chave candidata de B é uma chave candidata para a junção. h. As chaves candidatas para uma extensão arbitrária de A são iguais às chaves candidatas de A. i. As chaves candidatas para uma totalização arbitrária de A “por B” são as chaves candidatas de B. j. Toda chave candidata de A é uma chave candidata para a semijunção A SEMIJOIN B. k. Toda chave candidata de A é uma chave candidata para a semidiferença A SEMIMINUS B. Entretanto, muitas dadas declarações anteriores podem ser um pouco aprimoradas em certas situações. Por exemplo: • A combinação {F#,P#,J#} não é uma chave candidata para a restrição FPJ WHERE F# = F#(’F1’) — em vez disso, a combinação {P#,J#} é uma chave candidata. • Se A tem o cabeçalho {X,Y,Z} e a única chave candidata X, e também satisfaz à dependência funcional Y Z (consulte o Capítulo 10), então, Y é uma chave candidata para a projeção de A sobre Y e Z. • Se A e B são ambas restrições de C, então toda chave candidata de C é uma chave candidata de A UNION B. E assim por diante. Toda essa questão de inferência de chaves candidatas é discutida com algum detalhe na referência [10.6]. 8.6 Seja m o maior inteiro maior que ou igual a n/2. R terá o maior número possível de chaves candidatas se (a) cada conjunto distinto de m atributos é uma chave candidata, ou (b) n é impar e cada conjunto distinto de m-1 atributos é uma chave candidata. Em qualquer caso, segue-se que o número máximo de chaves candidatas em Ré n! / (m! * (n-m)! ). Nota: as variáveis de relações ELEMENTO e CASAMENTO na Seção 8.8 são dois exemplos de variáveis de relações com o número máximo possível de chaves candidatas. 8.7 R tem exatamente uma chave candidata, ou seja, o conjunto vazio de atributos {} (algumas vezes denotado por ). Nota: o

Page 44: 12 - Capítulo 8 - Integridade

conceito de chave candidata vazia (ou nulária) merece uma ligeira elaboração. Uma variável de relação como R, cujos únicos valores válidos são DEE e DUM não deve ter necessariamente nenhum atributo, e assim é “óbvio” que sua única chave candidata também não tem nenhum atributo. Porém, não são apenas as variáveis de relações sem atributos que podem ter tal chave candidata. Entretanto, se é uma chave candidata para alguma variável de relação R, então: • Ela deve ser a única chave candidata para R, porque qualquer Outro conjunto de atributos de R seria um superconjunto próprio de 4, violando assim a exigência de irredutibilidade para chaves candidatas. (Portanto, ela é de fato a chave primária, se for preciso escolher uma.) • R está restrita a conter no máximo uma tupla, porque toda tupla tem o mesmo valor para o conjunto vazio de atributos (ou seja, a tupla 0). Observe que nossa sintaxe certamente permite a declaração dessa variável de relação — por exemplo: VAR R BASE RELATION { PRIMARY KEY { } ; Ela também permite a declaração de uma variável de relação sem atributo algum — isto é, uma variável de relação cujos únicos valores possíveis são DEE e DUM: VAR R BASE RELATION PRIMARY KEY { } ; Voltando à questão de chaves candidatas vazias: é claro que, se uma chave candidata pode ser vazia, então uma chave estrangeira associada também pode ser vazia. A referência [5.51 discute com certo detalhe essa possibilidade. 8.8 Não há nenhuma regra “INSERT” explícita para chave estrangeira, porque as operações INSERT sobre a variável de relação referente (e também operações UPDATE sobre a chave estrangeira na variável de relação referente) são governadas pela própria regra básica de integridade referencial, isto é, a exigência de não haver valores de chave estrangeira não associados. Em outras palavras, tomando fornecedores e peças como exemplo concreto: • Uma tentativa de INSERT uma tupla de remessa (FP) só terá sucesso se (a) o número de fornecedor na tupla existir como um número de fornecedor em F, e (b) o número de peça nessa tupla existir como um nú 24 mero de peça em P.

• Uma tentativa de UPDATE uma tupla de remessa (FP) só terá sucesso se (a) o número de fornecedor na tupla atualizada existir como um número de fornecedor em F, e (b) o número de peça na tupla atualizada existir como um número de peça em P.

Page 45: 12 - Capítulo 8 - Integridade

Observe também com atenção que as observações anteriores se aplicam à variável de relação referente, enquanto as regras (explícitas) DELETE e UPDATE se aplicam à variável de relação referida. Portanto, falar sobre uma “regra INSERT” como se essa regra fosse algo semelhante às regras DELETE e UPDATE existentes é realmente algo bastante confuso. Esse fato fornece justificativa adicional para não incluirmos qualquer suporte de “regra INSERT” explícita na sintaxe concreta. 8.9 a. Aceita. b. Rejeitada (violação de unicidade de chave candidata). c. Rejeitada (viola a especificação RESTRICT). d. Aceita (o fornecedor F3 e todas as remessas para o fornecedor F3 são eliminadas). e. Rejeitada (viola a especificação RESTRICT). f. Aceita (o projeto J4 e todas as remessas para o projeto J4 são eliminados). g. Aceita. h. Rejeitada (violação de unicidade de chave candidata). i. Rejeitada (violação de integridade referencial). j. Aceita. k. Rejeitada (violação de integridade referencial). 1. Rejeitada (violação de integridade referencial — o número de projeto padrão jjj não existe na variável de relação J). 8.10 O diagrama referencial é mostrado na Figura 8.1. Segue-se uma definição de banco de dados possível. Para simplificar, não nos preocupamos em definir nenhuma restrição de tipo — exceto, é claro, pelo fato de que a especificação de POSSREP em uma determinada definição de tipo serve como uma restrição a priori do tipo. TYPE CURSO# POSSREP C CHAR ) ; TYPE TÍTULO POSSREP ( CHAR TYPE OFER# POSSREP C CHAR ) ; TYPE DATAOFER POSSREP ( DATE ) TYPE CIDADE POSSREP ( CHAR TYPE EMP# POSSREP ( CHAR ) ; TYPE NOME POSSREF’ ( NOME TYPE CARGO POSSREP ( CHAR TYPE GRADE POSSREP ( CHAR VAR CURSO BASE RELATION { CURSO# CURSO#, TÍTULO TÍTULO PRIMARY KEY ( CURSO# } ; VAR PREREQ BASE RELATION { SUPCURSO# CURSO#, SUB CURSO# CURSO# } PRIMARY KEY { SUP CURSO#, SUB CURSO# }

Page 46: 12 - Capítulo 8 - Integridade

FOREIGN KEY { RENAME SUP CURSO# AS CURSO# } REFERENCES CURSO ON DELETE CASCADE ON UPDATE CASCADE FOREIGN KEY { RENAME SUB CURSO# AS CURSO# } REFERENCES CURSO ON DELETE CASCADE ON UPDATE CASCADE ; 249

LOCAL PRIMARY FOREIGN

ENOME CARGO PRIMARY

VAR PROFESSOR { CURSO# OFER# EMP#

VAR MATRÍCULA { CURSO# OFER# EMP#

GRADE PRIMARY FOREIGN

CIDADE KEY { CURSO#, OFER# } KEY { CURSO# } REFERENCES CURSO ON DELETE CASCADE ON UPDATE CASCADE ;

NOME, CARGO KEY { EMP# } ;

BASE RELATION CURSO#, OFER#, EMP# }

OFER#, EMP# } OFER# } REFERENCES OFERTA ON DELETE CASCADE ON UPDATE CASCADE EMP# } REFERENCES EMPREGADO ON DELETE CASCADE ON UPDATE CASCADE ;

BASE RELATION MATRÍCULA CURSO#,

Page 47: 12 - Capítulo 8 - Integridade

OFER# EMP#,

CURSO#, OFER#, EMP# } CURSO#. OFER# } REFERENCES OFERTA ON DELETE CASCADE

ON UPDATE CASCADE REFERENCES EMPREGADO ON DELETE CASCADE ON UPDATE CASCADE ;

VAR OFERTA BASE RELATION { CURSO# CURSO#, OFER# OFER#, DATAOFER DATAOFER,

VAR EMPREGADO BASE RELATION { EMP# EMP#,

PRIMARY KEY { CURSO#, FOREIGN KEY { CURSO#,

FOREIGN KEY {

GRADE KEY { KEY {

FOREIGN KEY { EMP# }

SUPCURSO# SUB CURSO# ECURSO CURSO# CURSO#, OFER# CURSO#, OFER# - OFERTA

EMP#

EMPREGADO

EMP#

250 FIGURA 8.1 Diagrama referencial para o banco de dados de ensino

Surgem pontos importantes: 1. Os conjuntos de atributos (unitários) {CURSO#} em PROFESSOR e {CURSO#} em MATRÍCULA também poderiam ser considerados chaves estrangeiras, ambos fazendo referência a CURSO. Porém, se as restrições referenciais de PROFESSOR para OFERTA, MATRICULA para OFERTA e OFERTA para CURSO forem todas

Page 48: 12 - Capítulo 8 - Integridade

adequadamente mantidas, as restrições referenciais de PROFESSOR para CURSO e MATRICULA para CURSO serão mantidas automaticamente. Consulte a referência [8.101 para ver uma discussão adicional. 2. OFERTA é um exemplo de variável de relação que é simultaneamente referida e referente: existe uma restrição referencial para OFERTA de MATRICULA (de fato, também de PROFESSOR) e uma restrição referencial de OFERTA para CURSO: MATRICULA —* OFERTA —> CURSO 3. Note que existem dois caminhos referenciais distintos de MATRÍCULA para CURSO — um direto (chave estrangeira {CURSO#} em MATRICULA) e outro indireto, através de OFERTA (chaves estrangeiras {CURSO#, OFER#} em MATRICUL& e {CURSO#} em OFERTA): MATRÍCULA —* OFERTA —* CURSO Porém, os dois caminhos não são verdadeiramente independentes um do outro (o caminho superior está implícito na combinação dos dois inferiores). Para ver uma discussão adicional desse ponto, consulte novamente a referência [8.10]. 4. Também existem dois caminhos referenciais distintos de PREREQ para CURSO, mas dessa vez os dois caminhos são verdadeiramente independentes (eles têm significados totalmente distintos), consulte mais uma vez a referência [8.10]. 8.11 O diagrama referencial é mostrado na Figura 8.2. Observe que o banco de dados envolve um ciclo referencial (há um caminho referencial de cada uma das duas variáveis de relações para ela mesma). Além dessa consideração, a definição do banco de dados é essencialmente simples. Omitimos os detalhes.

FIGURA 8.2 Diagrama referencial envolvendo um ciclo 8.12 Mostramos apenas as definições de variáveis de relações (e essas apenas em esboço): VAR EMP BASE RELATION { EMP# . . . CARGO PRIMARY KEY { EMP# } VAR PGMR BASE RELATION { EMP# ... LING ... } PRIMARY KEY { EMP# } FOREIGN KEY { EMP# } REFERENCES EMP ON DELETE CASCADE ON UPDATE CASCADE ;

Page 49: 12 - Capítulo 8 - Integridade

GEREMP# 1

[EMP DEPTO______j

1 DEPTO#

Alguns pontos importantes: 1. Esse exemplo ilustra o fato de que uma chave estrangeira também pode ser uma chave candidata da variável de relação que a contém. A variável de relação EMP contém todos os empregados, e a variável de relação PGMR contém apenas os empregados que são programadores; assim, todo número de empregado que aparece em PGMR também deve aparecer em EMP (mas a recíproca não é verdadeira). A chave primária de PGMR é também uma chave estrangeira, referenciando a chave primária de EMP. 2. Observe que há outra restrição que precisa ser mantida nesse exemplo — ou seja, a restrição que um dado empregado aparecerá em PGMR se e somente se o valor de CARGO para esse empregado for “Programador”. E claro que essa restrição não é uma restrição referencial. 8.14 Observe que as soluções a. e b. a seguir são apenas aproximações para as soluções correspondentes mostradas na resposta ao Exercício 8.1. a. CREATE DOMAIN CIDADE CHAR(15) VARYING CONSTRAINT CIDADES VÁLIDAS CHECK ( VALUE IN ( Londres’, ‘Paris’, ‘Roma’, ‘Atenas’, ‘Oslo’, ‘Estocolmo, ‘Madri ‘ , ‘Amsterdâ ) ) ; b. CREATE DOMAIN F# CHAR(5) VARYING CONSTRAINT VALID F# CHECK SLJBSTRING ( VALUE FROM 1 FOR 1 ) = ‘E’ AND CAST ( SUBSTRING ( VALUE FROM 2 ) AS INTEGER ) >= O AND CAST ( SUBSTRINO ( VALUE FROM 2 ) AS INTEGER ) < 9999 c. CREATE ASSERTION SQL_C CHECK P.COR <> ‘Vermelho’ OR P.PESO < 50.0 d. CREATE ASSERTION SQLD CHECK ( NOT EXISTS ( SELECT * FROM J JX WHERE EXISTS ( SELECT * FROM J JY WHERE ( JX.J# <> JY.J# AND JX.CIDADE = JY.CIDADE ) ) ) ) e. CREATE ASSERTION SQLE CHECK ( ( SELECT COUNT(*) FROM F WHERE F.CIDADE = ‘Atenas’ ) < 2 f. CREATE ASSERTION SQL_F CHECK ( NOT EXISTS ( SELECT * FROM FPJ FPJX

Page 50: 12 - Capítulo 8 - Integridade

WHERE FPJX.QDE > 2 * ( SELECT AVG ( FPJY.QDE FROM FPJ FPJY ) ) ) g. CREATE ASSERTION SQL_G CHECK ( NOT EXISTS ( SELECT * FROM F EX WHERE EXISTS ( SELECT * FROM F FY WHERE SX.STATUS = ( SELECT MAX ( F.STATUS ) FROM F ) AND SY.STATUS = ( SELECT MIN ( F.STATUS FROM F ) AND FX.STATUS <> FY.STATUS AND FX.CIDADE = FY.CIDADE ) ) ) h. CREATE ASSERTION SQL_H CHECK ( NOT EXISTS ( SELECT * FROM F WHERE NOT EXISTS ( SELECT * FROM F WHERE F.CIDADE = J.CIDADE ) ) ) ;

252

CREATE ASSERTION SQLI CHECK ( NOT EXISTS ( SELECT * FROM J WHERE NOT EXISTS ( SELECT * FROM F WHERE F.CIDADE = J.CIDADE AND EXISTS ( SELECT * FROM FPJ WHERE FPJ.F# = F.F# AND FPJ.J# = J.J# ) ) ) ) j. CREATE ASSERTION SQL_J CHECK ( NOT EXISTS ( SELECT * FROM P OR EXISTS ( SELECT * FROM P WHERE P.COR = ‘Vermelho k. CREATE ASSERTION SQL_K CHECK ( SELECT AVG ( F.STATUS ) FROM F ) > 10 ) Se a tabela de fornecedores estiver vazia, o operador AVG de SQL retornará (incorretarnente!) um nulo, a expressão condicional será avaliada como desconhecida, e a restrição não será considerada violada. Consulte o Capítulo 18, que contém explicações adicionais. CREATE ASSERTION SQL_L CHECK ( NOT EXISTS ( SELECT * FROM F WHERE F.CIDADE = ‘Londres’ AND NOT EXISTS ( SELECT * FROM FPJ WHERE FPJ.F# = F.F# AND FPJ.P# = ‘P2’ ) m. CREATE ASSERTION SQLM CHECK ( NOT EXISTS ( SELECT * FROM P WHERE P.COR = ‘Vermelho1

Page 51: 12 - Capítulo 8 - Integridade

OR EXISTS ( SELECT * FROM P WHERE P.C0R = ‘Vermelho’ AND P.PESO < 50.0 n. CREATE ASSERTION SQLN CHECK ( ( SELECT COUNT(*) FROM P WHERE EXISTS ( SELECT * FROM FPJ WHERE EXISTS ( SELECT * FROM F WHERE ( P.P# = FPJ.P# AND FPJ.F# = FF# AND F.CIDADE = ‘Londres’ ) ) ) ) > ( SELECT COUNT(*) FROM P WHERE EXISTS ( SELECT * FROM FPJ WHERE EXISTS ( SELECT * FROM F WHERE ( P.P# = FPJ.P# AND FPJ.F# F.F# AND F.CIDADE = ‘Paris’ ) ) ) o. CREATE ASSERTION SQL_0 CHECK ( ( SELECT SUM ( FPJ.QDE ) FROM FPJ WHERE ( SELECT F.CIDADE FROM F WHERE F.F# FPJ.F# ) = ‘Londres’ ) > ( SELECT SUM ( FPJ.QDE ) FROM FPJ WHERE ( SELECT F.CIDADE FROM F WHERE F.F# FPJ.F# ) = ‘Paris’ ) p. Não pode ser feito. q. Não pode ser feito.

253