Nessa era da informação sob demanda não podemos nos contentar que os dados são apenas registros armazenados em um banco de dados. Sem a devida garantia de sua integridade também não é possível transformá-los em informação.

De nada adianta termos imensos bancos de dados armazenando incontáveis bytes de dados, que são constantemente inseridos, modificados, eliminados, se não podemos transformá-los em informação. E, de preferência, informação útil. E sem garantir que os dados armazenados estão íntegros, dificilmente conseguimos transformar esses bytes em informação real. Não importa quão avançada sejam suas ferramentas ou técnicas de aplicação em seu ambiente, nada irá adiantar se não houver um investimento na qualidade de seus dados.

Ao falarmos de integridade de dados, podemos citar alguns exemplos que podem e ajudam a garanti-la:

  • Firewalls
  • Sistema Gerenciador de Banco de Dados (SGDB)
  • Revisão regular de privilegio aos dados
  • Encriptação
  • Controle na prevenção de corrupção de dados
  • Compliance
  • Fácil recuperação de dados e performance
  • Integração com os sistemas de armazenamento (discos, fitas)
  • Tráfego dos dados pela rede
  • E mais...

Usando os exemplos citados acima, podemos dividir a integridade dos dados em dois grupos:

  • Sob a ótica do sistema:
    • integração com o hardware como discos, fitas;
    • conexão de rede;
    • firewalls;
    • integração entre o SGDB (Sistema Gerenciador de Banco de Dados) e o sistema.
  • Sob a ótica da aplicação:
    • integridade referencial;
    • triggers;
    • lockings;
    • controle de acesso aos dados.

Neste artigo será abordado a ótica da aplicação, mais precisamente da parte que depende do SGDB de integridade referencial, semântica e de domínio. Cada um desses itens será explicado no decorrer do texto.

O Sistema Gerenciador de Banco de Dados e a integridade dos dados

Os SQL statements INSERT, DELETE e UPDATE modificam as informações em um banco de dados existente. Toda vez que os dados são alterados existe a possibilidade da integridade lógica ser afetada. Por exemplo, a inserção de um produto inexistente em um pedido de cliente. E, ainda, um cliente pode ser eliminado mesmo tendo pedidos pendentes em seu nome. Em ambos os casos houve perda da integridade das informações

A integridade de dados em SGDBs é uma combinação de:

  • Integridade semântica: o dado de uma coluna sempre será do tipo de dado definido na criação da coluna
  • Integridade de entidade: cada linha de uma tabela possui um indicador que garanta a unicidade da mesma
  • Integridade referencial: os relacionamentos lógicos entre as entidades serão forçados pelos SGDB
  • Integridade de domínio: domínio é um conjunto de valores previamente definido no qual uma coluna só poderá conter valores pertencentes a esse domínio

Quando bem definido um banco de dados, provavelmente estes quatro conceitos de integridade serão aplicados.

Integridade Semântica

A integridade semântica garante que o dado inserido em uma linha da tabela seja um valor válido. Para esse valor ser válido deve ser do mesmo tipo de dados definido na especificação da coluna na tabela. Por exemplo, um atributo de uma determinada entidade definido como DATA só conterá dados relativos a DATA. É a certeza que no campo DATA_CONTRATACAO só terá datas válidas. Caso um SGDB permita a inserção de um outro tipo de dado diferente do definido, a integridade semântica será violada.

A integridade semântica em um SGDB é aplicada com a utilização de constraints. Usarei o termo em inglês porque não há uma tradução apropriada em português e, ainda, como os SGDBs na sua maioria são escritos em inglês, esse é o termo mais conhecido pelo mercado.

Constraints

Constraint pode ser definido resumidamente como uma regra que limita o valor que pode ser inserido, modificado ou eliminado em uma tabela. Na linguagem SQL temos os seguintes tipos de constraints:

  • Constraint de dados
  • Constraint NOT NULL (não nulo)
  • Constraint única
  • Constraint de validação (check constraint)

Segue abaixo um resumo sobre esses tipos de constraints.

Constraint de dados

Esse tipo de constraint pode ser considerado o mais simples e por muitas vezes ignorado como um constraint. Ele é o que delimita o tipo de dado de cada coluna em uma tabela. Os tipos de informações disponíveis na maioria dos SGDBs existentes pode ser dividia em:

  • Numérico
  • Alfanumérico ou caracteres
  • Data e tempo
  • Grandes objetos

Tipos de dados numéricos mais comuns:

Tipo de Dados Descrição
SMALLINT Números inteiros pequenos
INTEGER Números inteiros
DECIMAL Números decimais. Contem a parte inteira e a decimal (13,2 por exemplo)
REAL O número floating-point de precisão simples é um floating-point de 32 bit
FLOAT O número floating-point de precisão dupla é um floating-point de 64 bit
Tabela 1. Tipos de dados numéricos mais comuns.

Tipos de dados alfanumérico ou caracteres:

Tipo de Dados Descrição
CHAR Para cadeias fixas de caracteres alfanuméricos
VARCHAR Para cadeias fixas de caracteres alfanuméricos
Tabela 2. Tipos de dados alfanumérico mais comuns.

Tipos de dados data e tempo:

Tipo de Dados Descrição
DATE Data com valores representando dia, mês e ano
TIME Tempo com valores representando hora, minuto e segundo
TIMESTAMP Data e Tempo juntos com valores que representam ano, mês, dia, hora, minuto, segundo e milisegundo
Tabela 3. Tipos de dados data e tempo mais comum.

Observação: na perspectiva do usuário os tipos de dados, data e tempo parecem ser do tipo alfanumérico, porém fisicamente são armazenados como do tipo binário compactado.

Tipos de dados grandes objetos, tais como LOB (large object), armazenam grande quantidade de bytes e podem ser utilizados para armazenar objetos do tipo imagem ou som.

Constraint NOT NULL (não nulo)

O conceito de nulo é utilizado quando uma determinada coluna ou atributo de uma linha na tabela não possui valor ou este valor é desconhecido. Por outro lado, existem colunas / atributo que obrigatoriamente precisam de valor informado. Por exemplo, em uma tabela chamada FUNCIONARIO, onde estão dados de funcionários, o atributo NUMERO_FUNCIONARIO é obrigatório. Nesse caso é possível utilizar a constraint NOT NULL para garantir que haverá informação nessa coluna.

Importante frisar que NULO é diferente de brancos e zeros. Temos que lembrar também que tanto branco quanto zero são valores válidos e que são levados em conta em funções de coluna, tais como média, somatório, máximo, mínimo. Sendo que o NULO é desconsiderado nessas funções.

Constraint única

No SQL a constraint única é uma regra que garante que não haverá valores duplicados da mesma coluna ou em um conjunto de colunas na mesma tabela. Usando o mesmo exemplo da tabela FUNCIONARIO, podemos utilizar uma constraint única na coluna NUMERO_FUNCIONARIO para garantir que dois ou mais funcionários possuam o mesmo número de identificação.

Podemos considerar que a chave primaria (primary key), que será explicada mais adiante, é um tipo de constraint única. Lembrando que uma tabela pode ter apenas uma chave primária, porém diversas constraint únicas.

Constraint de validação (check constraint)

A constraint de validação determina um conjunto de valores permitidos para uma determinada coluna na tabela. Essas constraints são definidas explicitamente no DDL (data definition language) de uma tabela com expressões Booleanas similares a clausula WHERE do SQL. Constraint de validação é forçada em qualquer inserção ou atualização da coluna. Caso a inserção ou atualização da coluna não esteja de acordo com a definição da constraint, a mesma não será executada.

Por exemplo, vamos supor que a tabela FUNCIONARIO possua uma coluna SALARIO e que o valor do salário de cada funcionário não possa ser maior que 50.000,00, é possível criar uma constraint para erra regra:


CREATE TABLE FUNCIONARIO (

         NUMERO_FUNCIONARIO SMALLINT NOT NULL,

         SALARIO DECIMAL (9,2) NOT NULL CHECK SALARIO >= 50.000);

Constraint de validação pode ser muito útil para garantir regras de negócio, pois ela não pode ser sobreposta. Uma vez definida é dada a garantia que a regra será respeitada.

Utilizando esse tipo de integridade torna as suas aplicações mais robustas, consistentes e simples, pois não é necessário controlar as regras dentro do próprio código de programação ou utilizando uma subrotina. Dessa maneira é isolada em apenas um lugar a regra de negócio. Havendo a necessidade de mudar alguma regra de negócio, basta apenas alterar a constraint de validação na tabela ao invés de sair alterando códigos e mais códigos de programação uma vez que a mesma regra pode estar replicada em diversos pontos da sua aplicação.

Integridade Referencial

Em resumo, integridade referencial é um conceito de banco de dados que garante que todos os relacionamentos propostos entre tabelas no modelo de entidade-relacionamento (ER) serão respeitados dando a certeza que os dados de um banco de dados estarão íntegros. Esses relacionamentos são baseados nas definições de uma chave primária e uma chave estrangeira, além de regras pré-definidas para a manipulação dessas chaves.

Vale lembrar que existem diversos livros, manuais, tratados que falam sobre integridade referencial, sendo assim esse artigo apenas deseja explicar de uma maneira simples e com exemplos esse conceitos.

Conceitos de IR (Integridade Referencial)

A figura abaixo mostra alguns conceitos de IR e o uso de sua terminologia:

Conceitos de IR
Figura 1. Conceitos de IR.

Chave Candidata

Chave candidata é um atributo ou um conjunto de atributos de uma tabela que identifica uma única linha da tabela. A chave primária é extraída a partir do conjunto de chaves candidatas de uma tabela.

Na Figura 1 as chaves candidatas da tabela FUNCIONARIO o NR_FUNC (número do funcionário) e NR_CPF (número do CPF) porque eles garantem a unicidade de uma linha na tabela.

Chave Única

Esta chave é definida pela constraint única, garantindo que um atributo ou um conjunto de atributos não podem ser duplicados na mesma tabela.

Chave Primária

A chave primária de uma tabela é uma das chaves candidatas que melhor identifica uma tabela. Os critérios para escolher a chave primária são:

  • O conteúdo da coluna ou do conjunto de colunas deve sempre existir, em outras palavras deve possuir a constraint semântica NOT NULL.
  • O valor da chave não deve poder ser alterado.

A chave primária tem as seguintes características:

  • Apenas uma chave primária por tabela
  • Pode ser composta por mais de uma coluna
  • Obrigatoriamente ser NOT NULL (não nulo)

No exemplo mostrado na Figura 1 a coluna NR_FUNC da tabela foi escolhida como chave primária uma vez o NR_CPF, apesar de não usual, pode conter valor nulo já que no momento da contratação o funcionário pode não ter o CPF.

Na mesma figura, a chave primaria da tabela DEPARTAMENTO é NR_DEPT (número do departamento).

Chave Estrangeira

A chave estrangeira se refere ou é relacionada com alguma chave primaria de uma tabela, podendo ser inclusive da mesma tabela. É a concretização do relacionamento proposto no modelo entidade-relacionamento (MER).

A chave estrangeira tem as seguintes características:

  • Uma tabela pode possuir zero, uma ou diversas chaves primárias
  • Pode ser composta de uma ou mais colunas contando que ela seja exatamente igual à chave primaria que está relacionada (tipo de dado e tamanho)
  • Pode conter a clausula NULL

Na figura 1, a coluna NR_DEPT da tabela FUNCIONARIO é uma chave estrangeira que referencia a tabela DEPARTAMENTO cuja chave primária é NR_DEPT.

Chave Parente

A chave parente é uma chave única que é referenciada pela chave estrangeira. Pode ser tanto a chave primária como uma outra chave única existente na tabela. Os valores da chave parente determinam os valores válidos da respectiva chave estrangeira.

Tabela parente

Trata-se de uma tabela que possui chaves parentes que é referenciada por uma ou mais chaves estrangeiras.

A tabela DEPARTAMENTO da Figura 1 é uma tabela parente pois possui uma chave primária, NR_DEPT, que é referenciado por uma chave estrangeira na tabela FUNCIONARIO.

Normalmente as chaves estrangeiras são referenciadas por chaves primárias, mas nada impede que seja referenciada por uma chave única.

Tabela Dependente

Toda tabela que possui uma ou mais chaves estrangeiras é uma tabela dependente. Uma tabela dependente pode não possuir uma chave primária mas mesmo assim também pode ser uma tabela parente.

A tabela FUNCIONARIO é dependente da tabela DEPARTAMENTO por possuir uma chave estrangeira que referencia essa tabela.

Tabela Independente

Uma tabela que não possui chave estrangeira é chamada de Independente.

Linha Dependente

Trata-se de uma linha de uma tabela dependente no qual o valor da chave estrangeira não é nulo.

Integridade de entidade

A obrigatoriedade que uma chave primária não possa conter valores NULOS e que ela é única na tabela define a regra integridade de entidade.

Constraint Referencial

É a constraint que define a Integridade Referencial (IR) de relacionamentos entre tabelas em um banco de dados. O conceito de IR garante que todos os valores não nulos existentes em uma chave estrangeira são iguais aos da chave primária que ela referencia.

Regras e opções de IR

O modelo relacional possui um conjunto de regras para controle da inserção, atualização e eliminação dos dados nas tabelas. As regras são:

  • Regra de Inserção (INSERT)
  • Regra de Atualização (UPDATE)
  • Regra de Eliminação (DELETE)

Regra de Inserção

A regra de inserção é uma regra implícita, diferentemente das regras de atualização e eliminação que precisam ser declaradas no momento de criação das chaves estrangeiras.

Essa regra define que qualquer linha inserida na tabela dependente deve conter o valor na chave estrangeira no qual será:

  • Nulo, se for permitido nulo na chave estrangeira
  • Exatamente o mesmo valor da chave parente da respectiva tabela parente.

Independente se a tabela dependente possui uma ou mais chave parente, a regra de inserção garante que o valor inserido estará integro em relação aos seus relacionamentos.

Regra de Atualização

A regra de atualização deve ser explicitamente definida no momento de criação da chave estrangeira. Essa regra é acionada quando:

  • A chave estrangeira da tabela dependente é atualizada. Nesse caso, assim como a regra de inserção, o valor só pode ser alterado para nulo, caso seja permitido, ou para um valor existente na chave parente da tabela parente.
  • A chave primária da tabela parente é atualizada

Existem três opções para a regra de atualização:

  • RESTRITO:
    • A chave parente não pode ser atualizada caso exista alguma chave estrangeira em qualquer tabela dependente que o possua o mesmo valor.
  • CASCATA:
    • Caso a chave parente seja atualizada, todas as chaves estrangeiras respectivas de cada tabela dependente que contenha o valor da chave parente serão também atualizadas para o novo valor.
  • NULO:
    • Caso a chave parente seja atualizada, todas as chaves estrangeiras respectivas de cada tabela dependente que contenha o valor da chave parente serão atualizadas com o NULO.

Cada chave estrangeira é associada a sua regra de atualização respectiva. A regra de atualização determina se a atualização pode ser feita ou não. Sendo assim, uma chave parente não poderá ser atualizada caso qualquer chave estrangeira respectiva for definida com a regra RESTRITO.

Regra de Eliminação (delete)

Do mesmo modo que a regra de atualização, a regra de eliminação deve ser definida no momento da criação da chave estrangeira. Essa regra é imposta toda vez que houver tentativa de eliminar uma linha da tabela parente.

Essa regra também possui as mesmas três opções da regra de atualização:

  • RESTRITO:
    • A linha da tabela parente não pode ser eliminada caso exista na tabela dependente o mesmo valor na chave parente e de sua respectiva chave estrangeira.
  • CASCATA:
    • Todas as linhas de qualquer tabela dependente que possua o valor na chave estrangeira igual ao valor da chave parente também serão eliminadas.
    • A eliminação é propagada para os dependentes das tabelas dependentes.
    • Se uma das eliminações falharem o processo inteiro falha também.
  • NULO:
    • Todas as linhas de qualquer tabela dependente que possua o valor na chave estrangeira igual ao valor da chave parente serão atualizadas para o valor NULO.

Cada chave estrangeira possui sua própria regra de eliminação. A regra de eliminação determina se uma linha pode ser ou não eliminada. Portanto uma linha pode não ser eliminada caso:

  • Uma ou mais chave estrangeira possua a regra RESTRITO.
  • Quando possua a regra CASCATA e no momento da propagação da eliminação encontre alguma chave estrangeira que esteja definida como RESTRITO nas tabelas dependentes da tabela dependente.

A figura 2 mostra exemplo de um modelo de entidade-relacionamento com suas respectivas regras de eliminação:

MER com regras de eliminação
Figura 2 - MER com regras de eliminação.

Conclusão

No mundo atual, onde a informação vem se tornando cada vez valiosa, de nada adianta termos imensos bancos de dados contendo milhares de informações sem garantimos a integridade desses dados. A Integridade Referencial é um conceito que existe desde os primórdios do banco de dados e hoje entra como um papel fundamental para a garantia da veracidade das informações armazenadas.

Praticamente todos os Sistemas Gerenciadores de Banco de Dados (SGDB) possuem mecanismos para a implementação da IR facilitando o trabalho de programadores e analistas, pois, uma vez implementada existe a garantia que o SGDB irá gerenciá-la com todas essas regras em um único lugar encapsuladas.