Uso adequado de tabelas de pesquisa

25

Estou tendo problemas para descobrir exatamente como colocar bons limites para quando e onde usar as tabelas de pesquisa em um banco de dados. A maioria das fontes que procurei diz que nunca posso ter muitos, mas, em algum momento, parece que o banco de dados seria dividido em tantas partes que, embora possa ser eficiente, não é mais gerenciável. Aqui está um exemplo conjunto do que estou trabalhando:

Digamos que eu tenho uma tabela chamada Funcionários:

ID  LName   FName   Gender  Position
1   Doe     John    Male    Manager
2   Doe     Jane    Female  Sales
3   Smith   John    Male    Sales

Finja por um momento que os dados são mais complexos e contêm centenas de linhas. A coisa mais óbvia que vejo que poderia ser movida para uma tabela de pesquisa seria Posição. Eu poderia criar uma tabela chamada Positions e colar as chaves estrangeiras da tabela Positions na tabela Employees na coluna Position.

ID  Position
1   Manager
2   Sales

Mas até que ponto posso continuar dividindo as informações em tabelas de pesquisa menores antes que elas se tornem incontroláveis? Eu poderia criar uma tabela de Gênero e ter 1 correspondente a Masculino e 2 correspondente a Fêmea em uma tabela de pesquisa separada. Eu poderia até colocar LNames e FNames em tabelas. Todas as entradas "John" são substituídas por uma chave estrangeira 1 que aponta para a tabela FName que indica que um ID 1 corresponde a John. No entanto, se você descer muito longe dessa toca de coelho, sua tabela Funcionários será reduzida a uma bagunça de chaves estrangeiras:

ID  LName   FName   Gender  Position
1   1       1       1       1
2   1       2       2       2
3   2       1       1       2

Embora isso possa ou não ser mais eficiente para um servidor processar, isso certamente é ilegível para uma pessoa normal que pode estar tentando mantê-lo e dificulta o acesso de um desenvolvedor de aplicativos. Então, minha verdadeira pergunta é até que ponto é longe demais? Existem "práticas recomendadas" para esse tipo de coisa ou um bom conjunto de diretrizes em algum lugar? Não consigo encontrar informações on-line que realmente definam um conjunto de diretrizes úteis e úteis para esse problema em particular. O design do banco de dados é velho para mim, mas o BOM design do banco de dados é muito novo; portanto, respostas muito técnicas podem estar na minha cabeça. Qualquer ajuda seria apreciada!

Brad Turner
fonte
5
Usar tabelas de "pesquisa" é uma coisa. Substituir texto por números de identificação é uma coisa completamente diferente.
Mike Sherrill 'Cat Recall'
11
O sexo nem sempre pode ser fixado em 2 valores! Agora que temos transições de gênero, ou seja, um aplicativo pode não precisar de categorias adicionais como 'homem nascido agora mulher' ou 'mulher nascido agora homem' '.
@ Mike, bom comentário!
Walter Mitty 06/06
Na minha loja, os pensadores conseguiram parar depois de apenas quatro opções: masculino, feminino, transgênero, não divulgar.
Kevinsky

Respostas:

22

Mas até que ponto posso continuar dividindo as informações em tabelas de pesquisa menores antes que elas se tornem incontroláveis? Eu poderia criar uma tabela de Gênero e ter 1 correspondente a Masculino e 2 correspondente a Fêmea em uma tabela de pesquisa separada.

Você está misturando duas questões diferentes. Um problema é o uso de uma tabela de "pesquisa"; o outro é o uso de chaves substitutas (números de identificação).

Comece com esta tabela.

ID  LName   FName   Gender  Position
1   Doe     John    Male    Manager
2   Doe     Jane    Female  Sales
3   Smith   John    Male    Sales

Você pode criar uma tabela de "pesquisa" para posições como esta.

create table positions (
  pos_name varchar(10) primary key
);

insert into positions
select distinct position 
from employees;

alter table employees
add constraint emp_fk1
foreign key (position) 
  references positions (pos_name);

Sua tabela original é exatamente como era antes de criar a tabela "pesquisa". E a tabela de funcionários não requer associações adicionais para obter dados úteis e legíveis por humanos.

O uso de uma tabela de "pesquisa" se resume ao seguinte: Seu aplicativo precisa do controle sobre os valores de entrada fornecidos por uma referência de chave estrangeira? Nesse caso, você sempre pode usar uma tabela de "pesquisa". (Independentemente de usar uma chave substituta.)

Em alguns casos, você poderá preencher completamente essa tabela em tempo de design. Em outros casos, os usuários precisam poder adicionar linhas a essa tabela no tempo de execução. (E você provavelmente precisará incluir alguns processos administrativos para revisar novos dados.) O gênero, que na verdade tem um padrão ISO , pode ser completamente preenchido em tempo de design. Os nomes das ruas para pedidos internacionais de produtos on-line provavelmente precisam ser adicionados em tempo de execução.

Mike Sherrill 'Recorde Gato'
fonte
2
Eu não sabia que você poderia fazer tudo isso! A maneira como seu método funciona é meio bonita. Obrigado!
Brad Turner
4
Entrei no DBA Stack Exchange para poder votar nesta resposta. Isso é lindo e nunca me ocorreu. Obrigado!
CindyH
Aprecio o método para preencher a tabela de pesquisa. Minha razão para ler esta pergunta foi ver se haveria um benefício que não consegui ver com uma chave substituta em minhas tabelas de pesquisa. Você confirmou para mim que um único campo de texto é tão bom e útil quanto parece. Obrigado.
Sinthia V
8

Na tabela Empregados, eu só procuraria "Posição" porque é um conjunto limitado de dados que pode ser expandido.

  • Sexo é auto-descrevendo (dizer Mou F), limitado a 2 valores, e pode ser executada com uma restrição CHECK. Você não adicionará novos gêneros (ignorando besteiras de correção política)
  • O primeiro nome "John" não faz parte de um conjunto limitado e restrito de dados: o conjunto potencial de dados é massivo a ponto de ser efetivamente ilimitado, portanto não deve ser uma pesquisa

Se você deseja adicionar uma nova posição, basta adicionar uma linha à tabela de pesquisa. Isso também remove anomalias de modificação de dados, que é um ponto de normalização

Além disso, depois de ter um milhão de funcionários, é mais eficiente armazenar tinyint PositionID do que varchar.

Vamos adicionar uma nova coluna "moeda salarial". Eu usaria uma tabela de pesquisa aqui com uma chave CHF, GBP, EUR, USD etc: eu não usaria uma chave substituta. Isso pode ser restringido com uma restrição CHECK como Gender, mas é um conjunto de dados limitado e expansível, como Position. Dou este exemplo porque usaria a chave natural, mesmo que ela apareça em um milhão de linhas de dados de funcionários, apesar de serem char (3) e não tinyint

Então, para resumir, você usa tabelas de pesquisa

  1. onde você tem um conjunto de dados finito e expansível em uma coluna
  2. onde está não é auto-descritivo
  3. para evitar anomalias de modificação de dados
gbn
fonte
11
Um possível motivo para colocar o gênero em uma tabela de pesquisa é a localização.
a_horse_with_no_name 12/12
11
"Gênero ... (digamos M ou F), limitado a 2 valores ... ignorando besteiras de correção política" - Ironicamente, é a mesma correção política que você parece abominar que leva as pessoas a "gênero" incorretamente (' Masculino ',' Feminino ') quando eles significam "sexo" (' Masculino ',' Feminino '). Se o contexto for de gênero gramatical, geralmente há mais de dois valores. Se o contexto estiver registrando o sexo de um recém-nascido, haverá pelo menos quatro valores ('não foi oficialmente avaliado' e 'a avaliação oficial foi inconclusiva'). ps eu não quero soar duro, eu apreciei a ironia :)
onedaywhen
4
@ onedaywhen: o valor correto para uma coluna chamada "Sexo" é "Sim, por favor". A menos que você seja britânico
gbn
O termo "anomolias" está sendo mal utilizado aqui, pois o termo tem um significado particular diferente relacionado à normalização e o link é inadequado.
21414 philipxy
5

A resposta é "depende". Não é muito satisfatório, mas há muitas influências pressionando e puxando o design. Se você tiver programadores de aplicativos projetando o banco de dados, uma estrutura como você descreve funciona para eles porque o ORM oculta a complexidade. Você estará arrancando os cabelos quando escrever relatórios e precisará juntar dez tabelas para obter um endereço.

Projeto para o uso, uso pretendido e uso futuro provável. É aí que entra o seu conhecimento do processo de negócios. Se você estiver projetando um banco de dados para uma empresa veterinária, existem suposições razoáveis ​​sobre tamanho, uso e instruções de funcionalidade que serão bem diferentes de uma empresa de alta tecnologia.

Para reutilizar uma cotação favorita

"Um homem sábio me disse uma vez" normalize até doer, desnormalize até que funcione ".

Em algum lugar lá é o ponto ideal. Minha experiência foi que ter um ID de chave em mais de uma tabela não é um crime tão sério quanto alguns pensam se você nunca muda as chaves primárias.

Veja este exemplo abreviado de tabelas altamente normalizadas de um sistema real

CREATE TABLE PROPERTY
(ID                          NUMBER(9)           NOT NULL);

CREATE TABLE PROPERTY_TYPE
(ID                          NUMBER(9)           NOT NULL);

CREATE TABLE PROPERTY_LOCALE 
PROPERTY_ID                  NUMBER(9)           NOT NULL,
(LOCALE_ID                   NUMBER(9)           NOT NULL,  --language 
VALUE                        VARCHAR2(200)       NOT NULL);

CREATE TABLE PROPERTY_DEPENDENCY
(PROPERTY_ID                 NUMBER(9)           NOT NULL,
 PARENT_PROPERTY_ID          NUMBER(9)                   ,
 PROPERTY_TYPE_ID            NUMBER(9)           NOT NULL);

Essas tabelas configuram uma lista vinculada de propriedades únicas e propriedades pai-mãe e são usadas aqui

  CREATE TABLE CASE_PROPERTY
  (ID                        NUMBER(9)           NOT NULL,
  PARENT_ID                  NUMBER(9),
  CASE_ID                    NUMBER(9)           NOT NULL,
  PROPERTY_ID                NUMBER(9),
  PROPERTY_TYPE_ID           NUMBER(9)           NOT NULL);

Parece bom: obtenha todos os casos com um property_id em um único

Vamos pegar uma lista para escolher

 Select pl.value, pd.property_id
 from property_locale pl, property_dependency pd
 where pl.property_id = pd.property_id
 and pd.property_type_id = 2;  --example number

Agora tente selecionar todas as propriedades de um caso, se ele tiver property_types de 3 e 4 e 5, ou não ...

SELECT   cp2.case_id,
         (SELECT   pl.VALUE
            FROM   case_property cp, property_locale pl
           WHERE       cp.property_id = pl.property_id
                   AND CP.PROPERTY_TYPE_ID = 2
                   AND pl.locale_id = 2
                   AND cp.case_id = cp2.case_id)
            AS VALUE1,
         (SELECT   pl.VALUE
            FROM   case_property cp, property_locale pl
           WHERE       cp.property_id = pl.property_id
                   AND CP.PROPERTY_TYPE_ID = 34
                   AND pl.locale_id = 2
                   AND cp.case_id = cp2.case_id)
            AS VALUE2,
         (SELECT   pl.VALUE
            FROM   case_property cp, property_locale pl
           WHERE       cp.property_id = pl.property_id
                   AND CP.PROPERTY_TYPE_ID = 4
                   AND pl.locale_id = 2
                   AND cp.case_id = cp2.case_id)
            AS VALUE3
  FROM   case_property cp2
 WHERE   cp2.case_id = 10293  

Isso dói ... mesmo quando você usa maneiras mais elegantes de lidar com isso. No entanto, adicione um pouco de normalização quebrando propriedades para as quais um caso terá apenas um property_id e isso pode ser muito melhor.

Para descobrir quando você tem muitas tabelas ou não o suficiente, tente consultar o banco de dados com perguntas que o aplicativo usará, um relatório e uma análise ano a ano.

kevinsky
fonte
5
Os números de identificação não têm nada a ver com normalização. Só porque toda tabela tem um número de identificação não significa que esteja em 5NF, ou mesmo em 3NF. Significa apenas que você precisa fazer muitas associações para obter dados utilizáveis ​​dessa tabela.
Mike Sherrill 'Cat Recall'