ti-enxame.com

Por que o SELECT * é considerado prejudicial?

Por que SELECT * é uma prática ruim? Isso não significaria menos código para alterar se você adicionasse uma nova coluna que queria?

Eu entendo que SELECT COUNT(*) é um problema de desempenho em alguns bancos de dados, mas e se você realmente quisesse todas as colunas?

214
Theodore R. Smith

Existem três razões principais:

  • Ineficiência na movimentação de dados para o consumidor. Quando você SELECT *, muitas vezes você está recuperando mais colunas do banco de dados do que seu aplicativo realmente precisa para funcionar. Isso faz com que mais dados sejam movidos do servidor de banco de dados para o cliente, diminuindo o acesso e aumentando a carga de suas máquinas, além de levar mais tempo para percorrer a rede. Isso é especialmente verdadeiro quando alguém adiciona novas colunas a tabelas subjacentes que não existiam e não eram necessárias quando os consumidores originais codificavam o acesso a dados.

  • Problemas de indexação Considere um cenário em que você deseja ajustar uma consulta para um alto nível de desempenho. Se você usasse *, e retornasse mais colunas do que realmente precisava, o servidor teria que executar métodos mais caros para recuperar seus dados do que seria possível. Por exemplo, você não seria capaz de criar um índice que simplesmente cobrisse as colunas na sua lista SELECT, e mesmo se você fizesse (incluindo todas as colunas [shudder]), o próximo cara que aparecesse e adicionasse um Uma coluna para a tabela subjacente faria com que o otimizador ignorasse seu índice de cobertura otimizado e você provavelmente descobriria que o desempenho de sua consulta cairia substancialmente sem motivo aparente.

  • Binding Problems. Quando você seleciona *, é possível recuperar duas colunas do mesmo nome de duas tabelas diferentes. Isso geralmente pode causar falhas no seu consumidor de dados. Imagine uma consulta que une duas tabelas, ambas contendo uma coluna chamada "ID". Como um consumidor saberia qual era qual? SELECT * também pode confundir visões (pelo menos em algumas versões do SQL Server) quando estruturas de tabelas subjacentes mudam - a visualização não é reconstruída, e os dados que retornam podem ser absurdos . E a pior parte é que você pode tomar o cuidado de nomear suas colunas como você quiser, mas o próximo que aparecer não tem como saber que ele precisa se preocupar em adicionar uma coluna que colidirá com o seu já desenvolvido. nomes.

Mas nem tudo é ruim para o SELECT *. Eu uso liberalmente para esses casos de uso:

  • Consultas Ad-hoc. Ao tentar depurar algo, especialmente em uma tabela restrita com a qual eu talvez não esteja familiarizado, o SELECT * costuma ser meu melhor amigo. Isso me ajuda a ver o que está acontecendo sem ter que fazer um monte de pesquisas sobre quais são os nomes das colunas subjacentes. Isso chega a ser um "mais" maior quanto mais tempo os nomes das colunas obtiverem.

  • Quando * significa "uma linha". Nos casos de uso a seguir, SELECT * é bom, e os rumores de que é um assassino de desempenho são apenas lendas urbanas que podem ter alguma validade há muitos anos, mas don ' t agora:

    SELECT COUNT(*) FROM table;
    

    neste caso, * significa "contar as linhas". Se você usasse um nome de coluna em vez de *, contaria as linhas em que o valor dessa coluna não era nulo. COUNT (*), para mim, realmente mostra o conceito de que você está contando rows, e você evita casos estranhos de Edge causados ​​por NULLs sendo eliminados de seus agregados. 

    O mesmo acontece com este tipo de consulta:

    SELECT a.ID FROM TableA a
    WHERE EXISTS (
        SELECT *
        FROM TableB b
        WHERE b.ID = a.B_ID);
    

    em qualquer banco de dados que valha a pena, * significa apenas "uma linha". Não importa o que você coloca na subconsulta. Algumas pessoas usam o ID de b na lista SELECT, ou elas usam o número 1, mas as convenções do IMO são praticamente sem sentido. O que você quer dizer é "contar a linha", e é isso que significa *. A maioria dos otimizadores de consulta são espertos o suficiente para saber disso. (Embora, para ser sincero, eu apenas saiba que isso seja verdade com o SQL Server e o Oracle.)

279
Dave Markle

O caractere asterisco, "*", na instrução SELECT é uma abreviação para todas as colunas na (s) tabela (s) envolvidas na consulta. 

Atuação

A abreviação de * pode ser mais lenta porque:

  • Nem todos os campos são indexados, forçando uma varredura completa da tabela - menos eficiente
  • O que você salva para enviar o SELECT * pelo fio arrisca uma varredura completa da tabela
  • Retornando mais dados do que o necessário
  • Retornar colunas à direita usando o tipo de dados de comprimento variável pode resultar em sobrecarga de pesquisa

Manutenção

Ao usar SELECT *:

  • Alguém não familiarizado com o código base seria forçado a consultar a documentação para saber quais colunas estão sendo retornadas antes de poder fazer alterações competentes. Tornar o código mais legível, minimizando a ambigüidade e o trabalho necessário para pessoas não familiarizadas com o código, economiza mais tempo e esforço a longo prazo.
  • Se o código depender da ordem das colunas, SELECT * ocultará um erro que está prestes a acontecer se uma tabela tiver sua ordem de coluna alterada.
  • Mesmo se você precisar de todas as colunas no momento em que a consulta é gravada, isso pode não ser o caso no futuro
  • o uso complica a criação de perfil

Desenhar

SELECT * é umanti-pattern:

  • O objetivo da consulta é menos óbvio; as colunas usadas pelo aplicativo são opacas
  • Isso quebra a regra da modularidade sobre o uso de digitação estrita sempre que possível. Explicito é quase universalmente melhor. 

Quando "SELECT *" deve ser usado?

É aceitável usar SELECT * quando há a necessidade explícita de cada coluna na (s) tabela (s) envolvida (s), ao contrário de todas as colunas que existiam quando a consulta foi escrita. O banco de dados expandirá internamente o * para a lista completa de colunas - não há diferença de desempenho.

Caso contrário, liste explicitamente todas as colunas que serão usadas na consulta - preferencialmente enquanto estiver usando um alias de tabela.

84
OMG Ponies

Mesmo se você quiser selecionar todas as colunas agora, talvez não queira selecionar todas as colunas depois que alguém adicionar uma ou mais novas colunas. Se você escrever a consulta com SELECT *, estará correndo o risco de, em algum momento, alguém adicionar uma coluna de texto, o que faz com que sua consulta seja executada mais lentamente, mesmo que você não precise realmente dessa coluna.

Isso não significaria menos código para alterar se você adicionasse uma nova coluna que queria?

As chances são de que, se você realmente quiser usar a nova coluna, você terá que fazer muitas outras mudanças em seu código. Você está salvando apenas , new_column - apenas alguns caracteres de digitação.

18
Mark Byers

Se você nomear as colunas em uma instrução SELECT, elas serão retornadas na ordem especificada e, portanto, podem ser referenciadas com segurança pelo índice numérico. Se você usar "SELECT *", poderá acabar recebendo as colunas em seqüência arbitrária e, portanto, só poderá usar com segurança as colunas por nome. A menos que você saiba com antecedência o que você desejará fazer com qualquer nova coluna que seja adicionada ao banco de dados, a ação correta mais provável é ignorá-la. Se você for ignorar quaisquer novas colunas adicionadas ao banco de dados, não há nenhum benefício em recuperá-las.

4
supercat

Pense nisso como reduzindo o acoplamento entre o aplicativo e o banco de dados.

Para resumir o aspecto "cheiro de código":
SELECT * cria uma dependência dinâmica entre o aplicativo e o esquema. Restringir seu uso é uma maneira de tornar a dependência mais definida, caso contrário, uma alteração no banco de dados tem uma probabilidade maior de travar seu aplicativo. 

3
Kelly S. French

Em muitas situações, o SELECT * causará erros no tempo de execução no aplicativo, em vez de no tempo de design. Ele oculta o conhecimento de alterações de coluna ou referências ruins em seus aplicativos.

3
Andrew Lewis

Se você realmente quer cada coluna, eu não vi uma diferença de desempenho entre select (*) e nomeando as colunas. O driver para nomear as colunas pode ser simplesmente para ser explícito sobre quais colunas você espera ver em seu código.

Muitas vezes, porém, você não deseja que todas as colunas e o select (*) possam resultar em trabalho desnecessário para o servidor de banco de dados e informações desnecessárias devem ser passadas pela rede. É improvável que cause um problema perceptível, a menos que o sistema seja muito utilizado ou a conectividade de rede seja lenta.

3
brabster

Se você adicionar campos à tabela, eles serão incluídos automaticamente em todas as suas consultas em que você usar select *. Isso pode parecer conveniente, mas tornará seu aplicativo mais lento, já que você está buscando mais dados do que o necessário e, na verdade, ele irá travar seu aplicativo em algum momento.

Existe um limite para quantos dados você pode buscar em cada linha de um resultado. Se você adicionar campos às suas tabelas para que um resultado fique acima desse limite, você receberá uma mensagem de erro quando tentar executar a consulta.

Este é o tipo de erros que são difíceis de encontrar. Você faz uma mudança em um lugar e explode em algum outro lugar que realmente não usa os novos dados. Pode até ser uma consulta usada com menos frequência, de modo que demore um pouco até que alguém a use, o que dificulta ainda mais a conexão do erro à alteração.

Se você especificar quais campos deseja no resultado, estará protegido contra esse tipo de sobrecarga.

3
Guffa

Geralmente você tem que ajustar os resultados do seu SELECT * ... em estruturas de dados de vários tipos. Sem especificar em qual ordem os resultados estão chegando, pode ser complicado alinhar tudo adequadamente (e mais campos obscuros são muito mais fáceis de perder).

Desta forma, você pode adicionar campos às suas tabelas (mesmo no meio deles) por várias razões, sem quebrar o código de acesso sql em todo o aplicativo.

1
jkerian

Usar o SELECT * quando você precisa apenas de algumas colunas significa muito mais dados transferidos do que o necessário. Isso adiciona processamento no banco de dados e aumenta a latência na obtenção dos dados para o cliente. Adicione a isso que ele irá usar mais memória quando carregado, em alguns casos, significativamente mais, como grandes arquivos BLOB, é principalmente sobre a eficiência.

Além disso, no entanto, é mais fácil ver, ao examinar a consulta, quais colunas estão sendo carregadas, sem ter que procurar o que está na tabela.

Sim, se você adicionar uma coluna extra, ela seria mais rápida, mas na maioria dos casos, você precisaria/precisaria alterar seu código usando a consulta para aceitar as novas colunas de qualquer maneira, e há o potencial de conseguir as que você não usa. Deseja/espera pode causar problemas. Por exemplo, se você pegar todas as colunas, então confie na ordem em um loop para atribuir variáveis ​​e, em seguida, adicionar uma, ou se as ordens da coluna mudarem (visto acontecer ao restaurar a partir de um backup).

Este é também o mesmo tipo de raciocínio porque se você está fazendo um INSERT você deve sempre especificar as colunas.

1
Tarka

Eu não acho que realmente possa haver uma regra geral para isso. Em muitos casos, evitei o SELECT *, mas também trabalhei com estruturas de dados onde o SELECT * era muito benéfico.

Como todas as coisas, existem benefícios e custos. Eu acho que parte da equação benefício versus custo é quanto controle você tem sobre as estruturas de dados. Nos casos em que o SELECT * funcionava bem, as estruturas de dados eram rigidamente controladas (era um software de varejo), portanto não havia muito risco de alguém colocar um enorme campo BLOB em uma mesa. 

1
JMarsch

Referência retirada deste artigo

Nunca vá com "SELECT *",

Eu encontrei apenas uma razão para usar "SELECT *"

Se você tiver requisitos especiais e criar um ambiente dinâmico quando a coluna de adição ou exclusão for manipulada automaticamente pelo código do aplicativo. Neste caso especial, você não precisa alterar o código do aplicativo e do banco de dados, o que afetará automaticamente o ambiente de produção. Neste caso, você pode usar “SELECT *”.

1
Anvesh

RAZÕES POR QUE NÃO USAR SELECIONAR * DA TABELA: -

  1. E/S desnecessária

  2. Aumentar o tráfego da rede

  3. Vistas frágeis

  4. Conflito em consultas de associação

  5. Mais memória de aplicativo

  6. Arriscado durante a cópia de dados

  7. Depende da ordem da coluna

Sempre use nomes de colunas para ajudá-lo sempre em um banco de dados de grande escala.

0
shaurya uppal

Entenda seus requisitos antes de projetar o esquema (se possível).

Aprenda sobre os dados, 1) indexação 2) tipo de armazenamento usado, 3) mecanismo do fornecedor ou recursos; ie ... caching, capacidades in-memory 4) tipos de dados 5) tamanho da tabela 6) frequência de consulta 7) cargas de trabalho relacionadas se o recurso é compartilhado 8) Teste

A) Requisitos irão variar. Se o hardware não puder suportar a carga de trabalho esperada, você deverá reavaliar como fornecer os requisitos na carga de trabalho. Em relação à coluna de adição à tabela. Se o banco de dados oferecer suporte a visualizações, você poderá criar uma exibição indexada (?) Dos dados específicos com as colunas nomeadas específicas (vs. '*'). Revise periodicamente seus dados e esquemas para garantir que você nunca se deparará com a síndrome do "Lixo" -> "Lixo".

Assumindo que não há outra solução; você pode levar em consideração o seguinte. Há sempre várias soluções para um problema.

1) Indexação: O select * irá executar um tablescan. Dependendo de vários fatores, isso pode envolver uma busca de disco e/ou contenção com outras consultas. Se a tabela for de propósito múltiplo, garanta que todas as consultas tenham desempenho e sejam executadas abaixo dos tempos de destino. Se houver uma grande quantidade de dados e sua rede ou outro recurso não estiver ajustado; você precisa levar isso em conta. O banco de dados é um ambiente compartilhado.

2) tipo de armazenamento. Ou seja: se você estiver usando SSD, disco ou memória. Tempos de E/S e a carga no sistema/cpu variará.

3) O DBA pode ajustar o banco de dados/tabelas para um melhor desempenho? Assumindo por qualquer motivo, as equipes decidiram que o select '*' é a melhor solução para o problema; o DB ou a tabela pode ser carregado na memória. (Ou outro método ... talvez a resposta tenha sido projetada para responder com um atraso de 2-3 segundos? --- enquanto um anúncio toca para ganhar a receita da empresa ...)

4) Comece na linha de base. Entenda seus tipos de dados e como os resultados serão apresentados. Tipos de dados menores, número de campos reduz a quantidade de dados retornados no conjunto de resultados. Isso deixa recursos disponíveis para outras necessidades do sistema. Os recursos do sistema geralmente têm um limite; 'sempre' trabalhe abaixo desses limites para garantir estabilidade e comportamento previsível.

5) tamanho da tabela/dados. select '*' é comum em tabelas pequenas. Eles geralmente se encaixam na memória e os tempos de resposta são rápidos. Mais uma vez ... revise seus requisitos. Planejar o rastreamento de recursos; planeje sempre as necessidades futuras atuais e possíveis. 

6) Frequência de consulta/consultas. Esteja ciente de outras cargas de trabalho no sistema. Se essa consulta disparar a cada segundo, a tabela é pequena. O conjunto de resultados pode ser projetado para permanecer no cache/memória. No entanto, se a consulta for um processo em lote freqüente com Gigabytes/Terabytes de dados ... talvez seja melhor dedicar recursos adicionais para garantir que outras cargas de trabalho não sejam afetadas.

7) cargas de trabalho relacionadas. Entenda como os recursos são usados. A rede/sistema/banco de dados/tabela/aplicativo é dedicada ou compartilhada? Quem são as partes interessadas? Isso é para produção, desenvolvimento ou controle de qualidade? Isso é uma "solução rápida" temporária. Você já testou o cenário? Você ficará surpreso com quantos problemas podem existir no hardware atual hoje. (Sim, o desempenho é rápido ... mas o design/desempenho ainda está degradado.) O sistema precisa realizar 10K consultas por segundo versus 5-10 consultas por segundo. O servidor de banco de dados é dedicado ou o monitoramento de outros aplicativos é executado no recurso compartilhado. Alguns aplicativos/idiomas; O/S's consumirá 100% da memória causando vários sintomas/problemas.

8) Teste: Teste suas teorias e entenda o máximo que puder sobre. O seu problema de seleção '*' pode ser um grande problema ou pode ser algo com que você nem precisa se preocupar.

0
kllee

Há também uma razão mais pragmática: dinheiro. Quando você usa o banco de dados da nuvem e precisa pagar pelos dados processados, não há explicação para ler os dados que serão descartados imediatamente.

Por exemplo: BigQuery :

Preços de consulta

O preço da consulta refere-se ao custo de execução de seus comandos SQL e funções definidas pelo usuário. BigQuery cobra por consultas usando uma métrica: o número de bytes processados.

e Projeção de controle - Evite SELECT * :

Best practice: Control projeção - Consulta apenas as colunas que você precisa.

Projeção refere-se ao número de colunas que são lidas pela sua consulta. Projetar colunas em excesso incorre em E/S adicional (desperdiçada) e materialização (resultados de gravação).

Utilizar SELECT * é a forma mais cara de consultar dados. Quando você usa o SELECT *, o BigQuery faz uma verificação completa de todas as colunas da tabela.

0
Lukasz Szozda

Selecionar com o nome da coluna aumenta a probabilidade de que o mecanismo do banco de dados possa acessar os dados dos índices em vez de consultar os dados da tabela.

O SELECT * expõe seu sistema a alterações inesperadas de desempenho e funcionalidade no caso de alterações no esquema do banco de dados, pois você obterá novas colunas adicionadas à tabela, mesmo que seu código não esteja preparado para usar ou apresentar esses novos dados.

0
Aradhana Mohanty