Digamos que eu tenha esse esquema de banco de dados simplificado.
O esquema consiste em três tabelas, rua, cidade e CEP que têm uma relação muitos-para-muitos entre si.
O que eu quero fazer é uma previsão que consulte o banco de dados para possíveis correspondências, além de prever dados ausentes. Para colocar mais em um exemplo: digamos que o usuário comece a digitar uma cidade, talvez "Wash". Ele já tinha uma rua digitada, para eu não conhecer nenhuma rua, vamos chamar de "New Street Avenue". Esta é uma restrição , o que significa que deve ser cumprida em nossa previsão. Agora quero que a cidade consulte possíveis finalizadores de "Wash", bem como forneça dados ausentes, neste caso, CEPs, que tenham uma rua chamada "New Street Avenue"
Veja como seria a abordagem ingênua:
- Filtre a rua para todas as chaves cujo nome corresponda a "New Street Avenue"
- Filtre street_city para todas as chaves da rua e retorne as chaves da cidade
- Inner junte as chaves da cidade em city_zipcode e, em seguida, Inner junte-as no código postal
O problema com esta abordagem: O conhecimento entre rua e código postal é completamente ignorado. Isso significa que podemos acabar com CEPs e pares de cidades, onde sabemos que a cidade contém a rua que procuramos, mas o CEP não necessariamente . Esta seria uma previsão inválida para retornar, pois o endereço não existe!
Isso significa que preciso "persistir" as informações sobre a rua mais adiante.
Minha abordagem seria esta :
- Filtre a rua em busca das chaves que cumprem nossa condição.
- Inner Junte a rua na cidade para as chaves de #1. Mantenha ambas as colunas-chave.
- Inner join no city_zipcode mantendo as chaves do código postal também
- Filtre todos os pares de rua e CEP que não estão presentes em street_zipcode
(Em uma nota lateral: para "esperar" melhorar o desempenho, após cada "previsão", eu colocaria um comando LIMIT 10 (ou similar), pois precisamos apenas de uma quantidade sensata de previsões).
Essa abordagem deve funcionar. Pode não ser o melhor otimizado, mas retornaria os resultados corretos. No entanto, isso não apenas parece muito sujo para mim, mas também abre outro problema:
Tenho certeza de que escala horrivelmente, tanto no tamanho da mesa quanto nas tabelas adicionais
Há uma boa probabilidade de que haja outra tabela que tenha relacionamentos muitos para muitos, para fins de argumento, rua e CEP. Agora, se eu quiser pesquisar uma cidade com restrição de rua, preciso fazer o que fiz acima e adicionar ainda mais para filtrar também quaisquer relações entre rua e a nova tabela.
Sinto que estou entrando em um beco sem saída com essa abordagem. Eu adoraria alguém para me ajudar aqui sobre como abordar este problema melhor.
3 tabelas é o que eu chamo de "normalização excessiva" - isso leva a problemas de desempenho.
"Normalizar" é usado para dois propósitos, nenhum dos quais realmente se aplica ao seu caso de uso.
Isole algo, diga "cidade" para que possa ser facilmente alterado. Na vida real, isso raramente acontece nas cidades. ("Bombaim" -> "Mumbai" (Índia), "Hot Springs" -> "Verdade ou Consequências" (Novo México))
Economize espaço. Mesmo se você tiver todas as 3 milhões de cidades na Terra, soletrar o país a cada vez não é um fardo de espaço. (Para países, recomendo os códigos de país padrão de 2 letras e use
CHARACTER SET ascii
. Opcionalmente, tenha uma tabela mapeando-os para nomes soletrados.)CEPs: Nos EUA, existem cerca de 42 mil CEPs. Para normalizar, você pode usar um arquivo
SMALLINT UNSIGNED
. Mas o código postal, em si, pode ser armazenado em um arquivoMEDIUMINT(5) UNSIGNED ZEROFILL
. CEPs mudam. Mas isso geralmente envolve forçar metade dos usuários de um CEP a adotar um 'novo' CEP. Você teria que passar por todos os usuários desse CEP (que poderia serINDEXed
) para descobrir quais alterar.Da mesma forma para a separação da Iugoslávia e da Tchecoslováquia. Por outro lado, mudar "A República do Alto Volta" para "Burkina Faso" é fácil se você a 'normalizou'.
Plano A (milhões de localizações): Basta soletrar a localização de cada item.
Plano B (bilhões de locais): Dividido em dois: endereço e CEP+cidade+país
Pense de outra forma: o que você fará com o endereço?
SELECT SUM... GROUP BY country_code
. Isso implica quecountry_code
é uma coluna separada.