Em 25 de agosto de 2016 13:44, Guimarães Faria Corcete DUTRA, Leandro
<[email protected]> escreveu:
> Mas de fato há situações em que uma chave pode chegar a cobrir todos
> os atributos (naturais) de uma relação (não confundir com
> relacionamento).
E eu avalizo totalmente esta declaração. E ainda quero citar um
exemplo do porquê chaves naturais compostas DEVEM ser utilizadas
sempre que possível.
Vou citar um exemplo de um software de reservas desenvolvido para a
UTFPR campus DV e que está em uso agora em 2 campus. Inicialmente toda
a camada de acesso a dados utilizava Hibernate (JSF + Hibernate +C3P0)
com PostgreSQL 9.4 usando os famigerados "IDs" e chaves artificiais
autoincremento para tudo, para facilitar a integração com Hibernate.
Enquanto o software esteve rodando para 1 campus apenas (Dois
Vizinhos), não houveram muitos problemas.
Para encurtar o papo, quando começou a importação de dados de um tal
aSc TimeTables que define reservas de salas e horários por disciplinas
e turmas, começou a dor de cabeça. Os usuários importavam 2x o mesmo
XML e os "ID's" por não serem chaves naturais permitiam a duplicação
dos dados.
Pior ficou quando fiz testes para inclusão de um outro campus...
Um item que estava cadastrado para o campus DV (uma sala de aula, por
exemplo) poderia ser reservado em outro campus (erro de lógica). As
FK's e PK's não garantiam integridade lógica, apenas integridade
referencial (filho sem pai). A ideia central do sistema que era ficar
hospedado em um servidor apenas caiu por terra, foi necessário então
solicitar ao outro campus que hospedasse uma nova instância do
aplicativo.
Comecei a trabalhar no projeto com afinco. Refiz todo o modelo usando
chaves naturais e abandonei o Hibernate pelo sql2o. Deu um pouco mais
de trabalho no começo, mas depois tudo fluiu com uma naturalidade
fantástica. Ainda estou testando a nova versão e logo farei a migração
dos dados.
Como exemplo à declaração do Dutra, a tabela RESERVA_ITEM_AUTORIZACAO
possui 7 campos em sua chave primária, todos naturais que são todos os
campos da tabela. Ou seja, é uma tabela onde todos os campos são parte
da chave primária, sendo uma tabela "clássica" de relacionamento N*N.
Finalizando minha "história", com exceção de um índice único e 2
TRIGGERs para tratar horários conflitantes, toda a integridade lógica
ficou GARANTIDA apenas pelo uso das FK's e PK's com atributos
naturais, mesmo que com vários campos. E o número de FK's foi reduzido
para 1 terço do que havia antes, pois a propagação das chaves
compostas garante a integridade com a tabela "pai" de todos os níveis
superiores.
Abaixo segue o modelo atual no qual estou trabalhando (omiti demais
campos que não são chave):
CREATE TABLE pessoa (
/* PK - chave natural - código de cada servidor público */
matricula VARCHAR(20) NOT NULL,
(...)
);
CREATE TABLE instituicao (
/* PK - chave natural - código do MEC */
sigla VARCHAR(20) NOT NULL,
(...)
);
CREATE TABLE campus (
/* PK e FK tabela INSTITUICAO */
sigla_instituicao VARCHAR(20) NOT NULL,
/* PK - chave natural - código do MEC */
sigla_campus VARCHAR(20) NOT NULL,
(...)
);
/* "Cadastro" de itens */
CREATE TABLE item_reserva (
/* PK e FK tabela CAMPUS */
sigla_instituicao VARCHAR(20) NOT NULL,
/* PK e FK tabela CAMPUS */
sigla_campus VARCHAR(20) NOT NULL,
/* PK - chave natural - código de patrimonio interno do campus */
codigo_patrimonio VARCHAR(20) NOT NULL,
(...)
);
CREATE TABLE reserva (
/* PK e FK tabela ITEM_RESERVA */
sigla_instituicao VARCHAR(20) NOT NULL,
/* PK e FK tabela ITEM_RESERVA */
sigla_campus VARCHAR(20) NOT NULL,
/* PK - chave natural */
data_reserva_inicio TIMESTAMP NOT NULL,
/* PK - chave natural */
data_reserva_fim TIMESTAMP NOT NULL,
/* PK e FK tabela PESSOA */
matricula_usuario VARCHAR(20) NOT NULL,
(...)
);
/* Cada reserva pode incluir mais de um item, portanto aqui fica
separado da tabela reserva
Há aqui um indice único entre os campos
sigla_instituicao,
sigla_campus,
data_reserva_inicio,
codigo_patrimonio
Também há 2 triggers um pouco mais complexos que não permitem
horários conflitantes, algo impossível de tratar apenas com FKs.
Motivo: um mesmo item não pode ser reservado 2x na mesma
data e hora, ou enquanto uma reserva ainda está ativa */
CREATE TABLE reserva_item (
/* PK e FK tabela ITEM_RESERVA */
sigla_instituicao VARCHAR(20) NOT NULL,
/* PK e FK tabela ITEM_RESERVA */
sigla_campus VARCHAR(20) NOT NULL,
/* PK - chave natural */
data_reserva_inicio TIMESTAMP NOT NULL,
/* PK - chave natural */
data_reserva_fim TIMESTAMP NOT NULL,
/* PK e FK tabela PESSOA */
matricula_usuario VARCHAR(20) NOT NULL,
/* PK e FK tabela ITEM_RESERVA */
codigo_patrimonio VARCHAR(20) NOT NULL,
(...)
);
/* Alguns itens possuem um ou mais servidores responsáveies por
autorizar a reserva de um item (por exemplo, o ginásio
de esportes só pode ser autorizado pelo Diretor do
campus ou pelo vice-Diretor. Esta tabela armazena quem foi que
autorizou "aquela" determinada reserva solicitada. */
CREATE TABLE reserva_item_autorizacao (
/* PK e FK tabela ITEM_RESERVA */
sigla_instituicao VARCHAR(20) NOT NULL,
/* PK e FK tabela ITEM_RESERVA */
sigla_campus VARCHAR(20) NOT NULL,
/* PK - chave natural */
data_reserva_inicio TIMESTAMP NOT NULL,
/* PK - chave natural */
data_reserva_fim TIMESTAMP NOT NULL,
/* PK e FK tabela PESSOA */
matricula_usuario VARCHAR(20) NOT NULL,
/* PK e FK tabela ITEM_RESERVA */
codigo_patrimonio VARCHAR(20) NOT NULL,
/* PK e FK tabela PESSOA */
matricula_autorizacao VARCHAR(20) NOT NULL
);
TIAGO J. ADAMI
_______________________________________________
pgbr-geral mailing list
[email protected]
https://listas.postgresql.org.br/cgi-bin/mailman/listinfo/pgbr-geral