2010/1/2 MARCIO CASTRO <[email protected]>:
> Mas porquê?
>

Desculpe-me por querer responder.

Se você criar apenas uma chave primária artificial terá problemas
porque ela (propositadamente) não faz nenhum sentido com o domínio da
Relação e é gerada de forma a não se repetir. A chave artificial é
apenas um dado e com ela você não consegue tirar nenhuma informação ou
conclusão sobre a Entidade armazenada.

Imagine um cenário onde gostaríamos de armazenar as informações de
pessoas (nome e cpf). Podemos inserir pessoas com o mesmo nome mas não
com o mesmo CPF.

Criei uma tabela pessoas tendo como chave primária (artificial) um
coluna que é auto incrementada (cd_pessoa)

create table pessoas (
    cd_pessoa serial primary key,
    nome character varying not null,
    cpf character varying not null
);

Nesta tabela, podemos inserir as tuplas da seguinte maneira:
insert into pessoas (nome, cpf) values ('João', '123.123.123-21');

Mas como a chave primária é a coluna cd_pessoa, poderíamos inserir
novamente a mesma pessoa no banco.
insert into pessoas (nome, cpf) values ('João', '123.123.123-21');

cd_pessoa | nome        | cpf
1               | João         | 123.123.123-21
2               | João         | 123.123.123-21

Note que olhando para a chave primária não obtemos nenhuma informação.

Não usamos a chave natural cpf. Não podemos usá-la para distinguir um registro.
E como o Leandro escreveu:
>
> (...) criar aplicações sem poder usar chaves naturais é um terror.
>

Mais à frente escreverei sobre como tratar este problema.
------------------------------

"Tradicionalmente"(?) o mais adequado é encontrar qual a(s)
propriedade(s) da Entidade que queremos armazenar identifica esta como
sendo única. No caso CPF.

create table pessoas (
    cpf character varying primary key,
    nome character varying not null
);

Assim poderemos inserir pessoas com nomes idênticos mas com cpfs
distintos. Este é o comportamento desejado e aqui estamos usando
adequadamente a chave natural cpf.

insert into pessoas values('123.123.123-21', 'João');
insert into pessoas values('567.567.567-56', 'João');


Se nossa Relação for mais complexa, talvez uma propriedade não
determine a unicidade de uma Entidade armazenada, sendo necessário
usar mais de uma propriedade descrevendo assim uma chave composta.
Dependendo da Relação, a chave ficaria enorme, por exemplo, vendas.
Normalmente criamos uma Relação para as vendas efetuadas e outra
Relação para os itens vendidos.
Na relação que armazenamos as vendas teríamos que usar todas as
propriedades para identificar uma única venda pois não seria possível
identificá-la pelo cpf do vendedor, nem pelo cpf do vendedor mais o
cpf do cliente, nem pelo cpf do vendedor mais o cpf do cliente mais a
data...
Por isso existe um código. Uma chave artificial que encontramos em
toda Nota Fiscal.

------------------------------

Eu torno as coisas mais simples.

Criando as Relações usando chaves primárias artificiais e **tratando**
as naturais. (ênfase na palavra tratando)

A mesma Relação do primeiro exemplo.

create table pessoas (
    cd_pessoa serial primary key,
    nome character varying not null,
    cpf character varying NOT NULL UNIQUE
);

Desta maneira, tratando a chave natural, forçando os novos registros
ter um CPF único, não teremos o problema se tivéssemos apenas criado a
chave primária artificial.
Podemos trabalhar como se não existisse a chave artificial.
Poderíamos dropar a coluna "cd_pessoa" que a Relação ainda manteria a
consistência.

Eu disse tratando as (chaves) naturais no sentido de empregá-las na
lógica, de não esquecer delas, não deixá-las passarem despercebidas.

Fazendo desta forma, qualquer ORM "fundo de quintal" conseguirá fazer
os joins e de maneira geral identificar unicamente uma tupla.

Para entender o motivo pelo qual estes frameworks/bibliotecas (em
discussão os ORMs) que abstraem o banco de dados preferem uma única
propriedade, precisamos entender mais sobre
a programação orientada a objetos onde normalmente uma classe descreve
as propriedades (e métodos) que um objeto terá, sendo recomendado
especificar uma propriedade que identificará unicamente os objetos
instanciados da mesma classe.
Uma biblioteca ORM trata as "tabelas" de um banco de dados como sendo
classes e cada "registro" armazenado como um objeto, portanto, é de se
esperar um único atributo que descreva o registro/objeto.

Assim, os desenvolvedores destas bibliotecas criaram convenções
(padrões) para facilitar o trabalho de todos. Estas convenções são
como acordos entre desenvolvedores e usuários finais.
Cabe a nós decidir se vamos usar ou não estas bibliotecas e seguir
suas convenções. Esta é uma parte de toda controvérsia.

Muitos dos problemas que os desenvolvedores encontram com os ORMs é
tentar migrar o que existe (código e modelo do banco), feito fora das
convenções, principalmente quando o assunto é chaves compostas.

Algum tempo atrás, haviam criticas aos ORMs porque eram muito "burros"
e tinham comportamentos estúpidos gerando overheads como por exemplo:
Não fazer join: Para cada registro da esquerda fazer um select na da
direita. Dez registros na esquerda geravam dez selects separados na da
direita.
Deletar um por um: Para cada registro em muitos que devem ser
deletados uma query é feita.
Outra parte de toda controvérsia vem de coisas como estas que já não
são mais o problema.


Enfim, não use estas bibliotecas se você:
- Não quer ninguém ditando a sua forma de trabalhar.
- Quer ter controle total sobre o que é feito com o banco.
- Quer um sistema que faz uso eficiente de cada ciclo da CPU.
- Não quer dependências de código de terceiros.
- Não quer correr o risco de herdar bugs do código de terceiros.

Cara! Olhando pra esta lista da medo! ;)

-- 
Tarcisio F. Sassara
_______________________________________________
pgbr-geral mailing list
[email protected]
https://listas.postgresql.org.br/cgi-bin/mailman/listinfo/pgbr-geral

Responder a