Simplicity
Principal
Início
- - - - - - -
Programação
Administração
Modelagem
Projeto
Práticas
Scripts
Módulos Completos
- - - - - - -
Documentação
Dicas
Colaborações
Links
Ferramentas
- - - - - - -
Trabalho
Busca
Forum
Diversos
Contato
Sobre Min
Livro de Visitas
PG - RSS
feed image
 
Normalizando uma tabela de CEPs PDF Imprimir E-mail
Por Ribamar FS   
20 de July de 2008

Normalizando uma tabela de CEPs

Cenário atual:

Aproveitando o modelo de pessoa, vamos normalizar uma tabela de CEPs.

Tenho um arquivo CSV de ceps do tempo que os Correios distribiam gratuitamente em seu site, contendo
633.401 registros.
Agora vou usá-lo como exercício de normalização e tentar reaproveitar seus dados.

Esta tabela, ou melhor, após a normalização, serão algumas tabelas que poderão ser utilizadas
num cadastro de pessoas.

su - postgres

psql

create database cep encoding 'latin1';
Latin1 é para compatibilizar com conteúdo da tabela ceps.
Pois o recomendado atualmente é a codificação UNICODE. Em um banco com codificação
latin1 (iso-8859-1) tente representar por exemplo, o símbolo do euro (€).
Não consegue, pois é outra codificação, portanto sempre que possível devemos usar UTF-8.

Para comprovar crie essa tabela, num banco em latin1:
create table codificacao(c char(1))

Tente inserir este registro:
insert into codificacao values ('€')

E receberá a mensagem:
ERRO:  caracter 0xe282ac da codificação "UTF8" não tem equivalente em "LATIN1"


Tabela de CEPs original:

create table ceps
(
    cep char(8),
    tipo char(72),
    logradouro char(70),
    bairro char(72),
    municipio char(60),
    uf char(2)
);

Importar dados (script em: http://pg.ribafs.net/down/scripts//cep.sql.zip)
\copy ceps from /home/ribafs/cep_brasil.csv


Adicionar PK
alter table ceps add constraint cep_pk primary key(cep);


Tabela municipios

create sequence municipio_seq;
create table municipios as select distinct(municipio), uf from ceps order by uf;
alter table municipios rename column municipio to descricao;
alter table municipios add column municipio int;
update municipios set municipio=nextval('municipio_seq');
alter table municipios add constraint municipio_pk primary key(municipio);
alter table municipios add constraint municipio_unk unique(descricao);

municipios(descricao, uf, municipio)


Tabela bairros

create sequence bairro_seq;
create table bairros as select distinct(bairro) from ceps order by bairro;
alter table bairros rename column bairro to descricao;
alter table bairros add column bairro int;
update bairros set bairro=nextval('bairro_seq');
alter table bairros add constraint bairro_pk primary key(bairro);
alter table bairros add constraint bairro_unk unique(descricao);

bairros(descricao, bairro)


Tabela logradouros

create sequence logradouro_seq;
create table logradouros as select distinct(logradouro) from ceps order by logradouro;
alter table logradouros rename column logradouro to descricao;
alter table logradouros add column logradouro int;
update logradouros set logradouro=nextval('logradouro_seq');
alter table logradouros add constraint logradouro_pk primary key(logradouro);
alter table logradouros add constraint logradouro_unk unique(descricao);

logradouros(descricao, logradouro)


Tabela tipos

create sequence tipo_seq;
create table tipos as select distinct(tipo) from ceps order by tipo;
alter table tipos rename column tipo to descricao;
alter table tipos add column tipo int;
update tipos set tipo=nextval('tipo_seq');
alter table tipos add constraint tipo_pk primary key(tipo);
alter table tipos add constraint tipo_unk unique(descricao);

tipos(descricao, tipo)


Tabela ceps normalizada

create table cepsn
(
    cep char(8) not null,
    tipo int,
    logradouro int,
    bairro int,
    municipio int,
    primary key(cep, logradour),
    constraint tipo_fk foreign key (tipo) references tipos(tipo),
    constraint logradouro_fk foreign key (logradouro) references logradouros(logradouro),
    constraint bairro_fk foreign key (bairro) references bairros(bairro),
    constraint municipio_fk foreign key (municipio) references municipios(municipio)
);

-- Como atualmente podem existir mais de um CEP por logradouro, então CEP não pode ser a PK,
-- portanto teremos uma chave natural formada pelo CEP e pelo logradouro

Criar assim:

create table cepsn as select distinct(cep) from ceps
alter table cepsn add column tipo int;
alter table cepsn add column logradouro int;
alter table cepsn add column bairro int;
alter table cepsn add column municipio int;
alter table cepsn add constraint tipo_fk foreign key (tipo) references tipos(tipo);
alter table cepsn add constraint logradouro_fk foreign key (logradouro) references logradouros(logradouro);
alter table cepsn add constraint bairro_fk foreign key (bairro) references bairros(bairro);
alter table cepsn add constraint municipio_fk foreign key (municipio) references municipios(municipio);


Agora vamos popular a tabela cepsn com os registros da tabela ceps.
Veja que não é somente importar todos os tipos da tabela tipos para o campo tipo de cepsn.
Temos que trazer os tipos corretos de todos os 644 mil registros. Cada um com seu tipo correspondente.
Postanto não será uma tarefa simples nem direta. Exigirá um pouco de conhecimento da linguagem SQL.

Quando não temos ceerteza se a nossa consulta é coerente e que poderá demorar muito, então ajuda muito
consultar o PostgreSQL como ele faria essa consulta.
Execute a consulta com o EXPLAIN que ele vai dar uma dica, em espacial os valores do custo final e rows.

CEPs - 633401
Tipos - 189
Logradouros - 316499
Bairros - 16766
Municípios - 346

Atualizar tipo em cepsn com o valor tipo de tipos, mas correspondentes aos de ceps

Agora para receber os valores do campo tipo tenho que pensar assim:
tomar de cepsn o cep e testar se igual ao cep de ceps
Ainda pegar neste cep o valor do tipo em ceps e testar se é igual ao valor da descricao em tipos.
Então trazer o resultado. A consulta abaixo faz isso:

update cepsn cn set tipo = (select t.tipo from ceps c,tipos t where cn.cep = c.cep and c.tipo = t.descricao);

update cepsn cn set logradouro = (select l.logradouro from ceps c,logradouros l where cn.cep = c.cep and c.logradouro = l.descricao);
    Esta demora exageradamente.

Usei apenas:
update cepsn cn set logradouro = (select l.logradouro from ceps c,logradouros l where cn.cep = c.cep
    and c.logradouro = l.descricao and l.logradouro >= 305372 and l.logradouro <=305375);

Custo total: 10.609.291,16 Tempo: 41.419 ms

update cepsn cn set bairro = (select b.bairro from ceps c,bairros b where cn.cep = c.cep and c.bairro = b.descricao);
Usei somente:
update cepsn cn set bairro = (select b.bairro from ceps c,bairros b where cn.cep = c.cep and c.bairro = b.descricao
    and b.bairro < 9110 and b.bairro >9101);

update cepsn cn set municipio = (select m.municipio from ceps c,municipios m where cn.cep = c.cep and c.municipio = m.descricao);
Usei somente:
update cepsn cn set municipio = (select m.municipio from ceps c,municipios m where cn.cep = c.cep
    and c.municipio = m.descricao and m.uf='CE');

Obs.: só para ter uma idéia, o tempo desta consulta foi de 133.330ms e o curto total acusado pelo Explain era de 14.417.399.32 e
rows 633401 (total)

Agora, finalmente uma consulta de CEP na tabela normalizada
select cep, t.descricao, l.descricao, b.descricao, m.descricao, m.uf from cepsn c, tipos t,
    logradouros l, bairros b, municipios m where c.cep='60420440' and t.tipo=c.tipo and l.logradouro=c.logradouro
    and b.bairro=c.bairro and m.municipio=c.municipio;

Alterei a tabela cepsn adicionando índice único nos campos: tipo, bairro, logradouro e municipio:
CREATE UNIQUE INDEX unq_tipo ON pepsn (tipo);

Consultas úteis:
select * from municipios group by uf,municipio,descricao having count(municipio) = 1 order by uf,descricao;
select count(municipio) from municipios group by uf having count(municipio)>1 and uf='CE';

Cuidado com operadores boolean:
select * from municipios where uf='SP' and uf='DF' order by descricao;  -- Nada retorna
select * from municipios where uf='SP' or uf='DF' order by descricao; -- esta retorna

Comentários
Adicionar Pesquisar
Escrever comentário
Nome:
Email:
 
Website:
Título:
UBBCode:
[b] [i] [u] [url] [quote] [code] [img] 
 
 
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch:
:(:shock::X:side::):P:unsure::woohoo::huh::whistle:;):s
:!::?::idea::arrow:
 
Please input the anti-spam code that you can read in the image.

3.25 Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

Última Atualização ( 22 de July de 2008 )
 
< Anterior   Próximo >
© 2008 PostgreSQL - Curso Online
Joomla! is Free Software released under the GNU/GPL License.
Design by DeiseHost.Com