Em 25 de agosto de 2016 13:44, Guimarães Faria Corcete DUTRA, Leandro <l...@dutras.org> 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 pgbr-geral@listas.postgresql.org.br https://listas.postgresql.org.br/cgi-bin/mailman/listinfo/pgbr-geral