Flag vs table split

10

Estou projetando uma tabela de itens que (potencialmente) conterá dezenas de milhões de registros. Alguns itens não estarão disponíveis para uso até que sejam "aprovados" pelo administrador. Por "uso", quero dizer que esses itens não serão referenciados em nenhuma outra tabela até que sejam "aprovados". Até 50% dos itens podem ser "não aprovados" a qualquer momento. Os registros podem se tornar "aprovados", mas não vice-versa.

Eu considero duas opções de design:

  • uma bandeira pouco
  • uma tabela separada de itens "não aprovados" - quando o item é aprovado, ele é movido para a tabela "regular" (a renovação do ID do item não é um problema)

Eu acho que a segunda opção é muito melhor. O sinalizador de bit leva apenas um byte por linha, portanto não é um problema. Porém, se temos um milhão de registros aprovados e um milhão de registros não aprovados na mesma tabela - o tempo de varredura aumenta para operações com registros aprovados.

A pergunta é: devo considerar a primeira opção (sinalizador de bit)? Tem algum benefício na situação descrita?

Dima
fonte
11
Pode ser útil lembrar que você pode usar índices filtrados para acelerar o acesso aos registros aprovados. brentozar.com/archive/2013/11/…
mendosi
Infelizmente, índices filtrados não são usados ​​em consultas parametrizadas.
Dima
@ Dima isso não é totalmente verdade. Se um índice filtrado tem uma palavra WHERE status='A'e uma consulta WHERE status = 'A' AND (... other columns and parameters here...), então o índice ainda pode ser usado.
precisa saber é o seguinte

Respostas:

6

Você pode ter os dois lados com vistas particionadas .

Você cria uma tabela subjacente para cada status, imposta por restrições, com valores mutuamente exclusivos. Em seguida, uma visão que une as tabelas subjacentes. A visualização ou cada tabela base pode ser referenciada explicitamente. Se o status de uma linha for UPDATEd através da visualização, o DBMS o excluirá de uma tabela base e a inserirá na correspondente ao novo status. Cada tabela base pode ser indexada independentemente, de acordo com seu padrão de uso. O otimizador resolverá as referências de índice para uma única tabela base correspondente, se puder.

Os benefícios são:
a) índices mais rasos. Faça as contas no fan-out do índice, no entanto. Nessa escala e divididos entre os valores de status, é possível que os índices tenham a mesma profundidade nas tabelas divididas e na tabela combinada.
b) nenhum código de aplicativo precisa ser alterado. Os dados continuam a aparecer como um todo contínuo.
c) novos valores futuros de status podem ser incluídos adicionando uma nova tabela base, com restrições e recriando a visualização.

O custo é todo esse movimento de dados; duas páginas e índices associados são gravados para cada atualização de status. Muito IO para lidar. Esse movimento também causará fragmentação.

Michael Green
fonte
5

uma tabela de itens que (potencialmente) conterá dezenas de milhões de registros.

Na verdade, não é tanto assim, considerando o que o SQL Server pode lidar com eficiência. Obviamente, lembro-me de um dos meus trabalhos anteriores, em que uma das maiores tabelas (um sistema de instância única) tinha 2 milhões de linhas, e foi com isso que eu já lidei. Em seguida, o próximo trabalho teve 17 instâncias de produção, com algumas tabelas com centenas de milhões de linhas e que foram agregadas em um Data Warehouse com várias tabelas de fatos com mais de 1 bilhão de linhas. Não me interpretem mal, não estou zombando de dezenas de milhões de linhas, apenas enfatizando que, com um bom modelo de dados e uma indexação adequada (e manutenção de índice), o SQL Server pode lidar muito .

Até 50% dos itens podem ser "não aprovados" a qualquer momento.

Hmm. Isso não parece certo. A taxa de "aprovar" entradas será metade da taxa de obtenção de novas entradas? Para cada 2 novas entradas, apenas 1 será "aprovado"? No seu exemplo de 2 milhões de linhas e 1 milhão cada para "aprovado" e "não aprovado", alguns anos depois com outros 10 milhões de entradas, você espera 6 milhões cada para "aprovado" e "não aprovado"? Ou será que os 1 milhão de "não aprovados" permanecerão um pouco constantes, de modo que, com 10 milhões de novas entradas, haverá 11 milhões de "aprovados" e ainda 1 milhão de "não aprovados"?

Os registros podem se tornar "aprovados", mas não vice-versa.

Isso é verdade hoje , mas as coisas mudam com o tempo e, portanto, sempre há a possibilidade de a empresa decidir permitir "não aprovação" ou talvez algum outro status, como "arquivado", etc.

Então, vejamos as opções:

Sinalizador (ou possivelmente TINYINT"status")

  • Um pouco mais lento para consultas de cada status
  • Mais flexível ao longo do tempo / fácil de incorporar uma alteração como um terceiro estado (por exemplo, "Arquivado") com apenas um novo valor de status de Pesquisa. Nenhuma nova tabela (necessariamente), algum novo código, apenas algum código atualizado.
  • Menos trabalho (ou seja, código, teste etc.) e menos espaço para erro ao atualizar uma única TINYINTcoluna
  • Menos complicado = menor custo de manutenção ao longo do tempo, menor tempo de treinamento para os novos funcionários descobrirem
  • (possivelmente) Menor impacto no log de transações quando uma tabela é atualizada
  • Só precisa de uma tabela de pesquisa para "RecordStatus" e FK entre as duas tabelas.

Duas tabelas separadas (uma para "aprovado" e uma para "não aprovado")

  • Um pouco mais rápido para consultas de cada status
  • Menos flexível ao longo do tempo / mais difícil de incorporar uma mudança como um terceiro estado (por exemplo, "Arquivado"); Um novo estado exigiria provavelmente outra tabela e, definitivamente, um código novo e atualizado.
  • Mais trabalho (código, teste, etc.) e mais espaço para erros ao mover registros da tabela "Não aprovado" para a tabela "Aprovado"
  • Mais complicado = custos de manutenção mais altos ao longo do tempo, mais tempo de treinamento para os novos funcionários descobrirem
  • (possivelmente) Maior impacto no log de transações quando uma tabela é excluída e outra é inserida
  • Não há necessidade de se preocupar com a " renovação da ID do item de ": a tabela não aprovado tem coluna ID, que é uma IDENTITYcoluna e tabela aprovada tem coluna de ID que é não um IDENTITY(uma vez que não é necessário lá). Portanto, os valores de ID permanecem consistentes à medida que o registro se move entre as tabelas.

Pessoalmente, eu me inclinaria para a única tabela com StatusIDcoluna para começar. Usar duas tabelas parece uma otimização prematura complicada demais. Esse tipo de otimização pode ser discutido se / quando o número de registros estiver em várias centenas de milhões e a indexação não fornecer nenhum ganho de desempenho.

Solomon Rutzky
fonte
É uma tabela com dados em movimento rápido: muitas vezes preenchida com muitas linhas novas, muitas vezes as linhas são excluídas. Tentei remover todos os detalhes (como decisão de negócios, codificação do cliente etc.) para me concentrar apenas em um único tópico. Basicamente, temos a tabela de design antigo com uma bandeira pouco. E sei 100% que as linhas em que o sinalizador está definido como 1 nunca são usadas em nenhuma outra tabela. Então, sinto que eles só acontecem lá e podem ser movidos para uma mesa separada. A tabela é varrida quase todas as consultas para o banco de dados. Portanto, reduzir seu "peso" potencialmente pode reduzir as operações de CPU / IO.
Dima
3
Outra vantagem das tabelas divididas: você pode ter FKs que fazem referência apenas à tabela "Aprovada".
precisa saber é o seguinte
O outro problema com tabelas divididas para uma única entidade é a integridade da restrição. Referências de outras tabelas não serão boas com o movimento do disco. Isso exigirá código a ser escrito para contornar esses problemas, tais como tabelas de referência de espelho para a mesa de split -> muito problemático
user1567453