Por que o SELECT * é considerado prejudicial?


256

Por que é uma SELECT *má prática? Não significaria menos código para alterar se você adicionasse uma nova coluna que desejava?

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


30
SELECT COUNT(*)ser mau é incrivelmente velho e desatualizado . Para informações sobre SELECT *- consulte: stackoverflow.com/questions/1960036/…
OMG Ponies

8
SELECT COUNT(*)fornece uma resposta diferente SELECT COUNT(SomeColumn), a menos que a coluna seja uma coluna NOT NULL. E o otimizador pode dar SELECT COUNT(*)tratamento especial - e geralmente dá. Observe também que WHERE EXISTS(SELECT * FROM SomeTable WHERE ...)é dado tratamento de caso especial.
Jonathan Leffler

3
@ Michael Mrozek, na verdade, é o inverso da pergunta. Estou perguntando se é sempre prejudicial, não se nunca foi prejudicial.
Theodore R. Smith

1
@Bytecode Ninja: especificamente, o MySQL com o mecanismo MyISAM possui uma otimização para COUNT (*): mysqlperformanceblog.com/2007/04/10/count-vs-countcol
Piskvor saiu do prédio

Respostas:


312

Na verdade, existem três razões principais:

  • Ineficiência na movimentação de dados para o consumidor. Quando você seleciona *, geralmente recupera mais colunas do banco de dados do que o 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 em suas máquinas, além de levar mais tempo para viajar pela rede. Isso é especialmente verdade quando alguém adiciona novas colunas às tabelas subjacentes que não existiam e não eram necessárias quando os consumidores originais codificaram seu 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 ele retornasse mais colunas do que realmente precisava, o servidor frequentemente precisaria executar métodos mais caros para recuperar seus dados do que poderia. Por exemplo, você não seria capaz de criar um índice que simplesmente cobrisse as colunas da sua lista SELECT, e mesmo se o fizesse (incluindo todas as colunas [ estremecer ]), o próximo cara que apareceu e adicionou uma coluna ao subjacente A tabela faria com que o otimizador ignorasse seu índice de cobertura otimizado e você provavelmente descobriria que o desempenho da sua consulta diminuiria substancialmente sem motivo aparente.

  • Problemas de encadernação. Quando você seleciona *, é possível recuperar duas colunas com o mesmo nome de duas tabelas diferentes. Isso pode travar seu consumidor de dados. Imagine uma consulta que junte duas tabelas, ambas contendo uma coluna chamada "ID". Como um consumidor saberia qual era qual? SELECT * também pode confundir visualizações (pelo menos em algumas versões do SQL Server) quando as estruturas de tabela subjacentes são alteradas - a exibição não é reconstruída e os dados retornados podem ser absurdos . E a pior parte disso é que você pode nomear suas colunas como quiser, mas o próximo cara que aparecer não terá como saber que ele precisa se preocupar em adicionar uma coluna que colidirá com o seu já desenvolvido. nomes.

Mas não é de todo ruim para o SELECT *. Eu o uso liberalmente para estes casos de uso:

  • Consultas ad-hoc. Ao tentar depurar alguma coisa, especialmente em uma mesa estreita com a qual talvez eu não esteja familiarizado, o SELECT * geralmente é meu melhor amigo. Isso me ajuda a ver o que está acontecendo, sem ter que fazer muita pesquisa sobre quais são os nomes das colunas subjacentes. Isso passa a ser um "mais" maior, quanto mais os nomes das colunas ficarem.

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

    SELECT COUNT(*) FROM table;

    neste caso, * significa "contar as linhas". Se você usasse um nome de coluna em vez de *, ele contaria as linhas em que o valor dessa coluna não era nulo . COUNT (*), para mim, realmente leva à tona o conceito de que você está contando linhas e evita que casos estranhos causados ​​por NULLs sejam 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 se preze, * significa apenas "uma linha". Não importa o que você coloca na subconsulta. Algumas pessoas usam o ID de b na lista SELECT ou usam o número 1, mas na IMO essas convenções são praticamente sem sentido. O que você quer dizer é "contar a linha", e é isso que * significa. A maioria dos otimizadores de consulta existentes é inteligente o suficiente para saber disso. (Embora, para ser sincero, só sei que isso é verdade no SQL Server e no Oracle.)


17
O uso de "SELECT id, nome" é tão provável quanto "SELECT *" para selecionar duas colunas com o mesmo nome em duas tabelas diferentes ao usar junções. A prefixação com o nome da tabela resolve o problema nos dois casos.
Michał Tatarynowicz

1
Eu sei que isso é mais antigo, mas é o que foi puxado enquanto pesquisava no Google, então estou perguntando. "Quando * significa" uma linha ". Nos casos de uso a seguir, SELECT * é bom, e os rumores de que é um matador de desempenho são apenas lendas urbanas ..." você tem alguma referência aqui? Esta afirmação é devido ao hardware ser mais poderoso (se for esse o caso, isso não significa que não é ineficiente, apenas que é menos provável que você o note). Não estou tentando adivinhar por si só; estou apenas imaginando de onde vem essa afirmação.
Jared

6
No que diz respeito às referências, você pode examinar os planos de consulta - eles são idênticos nos casos em que você tem um "*" na subconsulta e quando você seleciona uma coluna. Eles são idênticos porque o otimizador baseado em custos "reconhece" que, semanticamente, você está falando de qualquer linha que satisfaça os critérios - não é uma questão de hardware ou velocidade.
Dave Markle

4
Mais uma vantagem do uso *é que, em algumas situações, ele pode tirar melhor proveito dos sistemas de cache do MySQL. Se você estiver executando um grande número de semelhantes selectconsultas que solicitam diferentes nomes das colunas ( select A where X, select B where X...) usando uma select * where Xpermitirá que o cache para lidar com um número maior de consultas que pode resultar em um aumento de desempenho substancial. É um cenário específico de aplicativo, mas vale a pena lembrar.
Ben D

2
Mais de 8 anos depois, mas deseja acrescentar um ponto sobre ambiguidade que não foi mencionado. Trabalhando com mais de 200 tabelas em um banco de dados e tendo uma mistura de convenções de nomenclatura. Ao revisar o código que interage com os resultados da consulta, SELECT *força os desenvolvedores a examinar o (s) esquema (s) da tabela envolvido (s), a fim de determinar as colunas afetadas / disponíveis, como dentro de um foreachou serialize. A tarefa de analisar repetidamente os esquemas para rastrear o que está acontecendo aumentará inevitavelmente o tempo total envolvido na depuração e no desenvolvimento de códigos relacionados.
Fyrye 02/02/19

91

O caractere asterisco, "*", na instrução SELECT, é um atalho para todas as colunas nas tabelas envolvidas na consulta.

atuação

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

  • Nem todos os campos são indexados, forçando uma verificação completa da tabela - menos eficiente
  • O que você salva para enviar SELECT *por cabo, corre o risco de uma verificação completa da tabela
  • Retornando mais dados do que o necessário
  • Retornar colunas finais 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 a base de código 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 ambiguidade 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 esperando que aconteça se uma tabela tiver sua ordem das colunas alterada.
  • Mesmo se você precisar de todas as colunas no momento em que a consulta for gravada, isso pode não ser o caso no futuro
  • o uso complica a criação de perfil

Projeto

SELECT *é um anti-padrão :

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

Quando "SELECT *" deve ser usado?

É aceitável usar SELECT *quando houver a necessidade explícita de todas as colunas nas tabelas envolvidas, em oposição a todas as colunas existentes quando a consulta foi gravada. O banco de dados expandirá internamente o * na 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 - de preferência ao usar um alias de tabela.


20

Mesmo se você quiser selecionar todas as colunas agora, poderá não querer selecionar todas as colunas depois que alguém adicionar uma ou mais novas colunas. Se você escrever a consulta, SELECT *corre o risco de que em algum momento alguém possa adicionar uma coluna de texto, o que torna sua consulta mais lenta, mesmo que você não precise dessa coluna.

Não significaria menos código para alterar se você adicionasse uma nova coluna que desejava?

As chances são de que, se você realmente deseja usar a nova coluna, precisará fazer muitas outras alterações no seu código. Você está apenas salvando , new_column- apenas alguns caracteres de digitação.


21
Especialmente se que a nova coluna é um blob três megabyte
Matti Virkkunen

2
@ Mati - Mas espero que eles pensem mais do que "Ei, vamos colocar uma enorme coluna BLOB nesta mesa!" . (Sim uns bobos espero que eu sei, mas não pode um sonho cara?)
ChaosPandion

5
O desempenho é um aspecto, mas geralmente também há um aspecto de correção: a forma do resultado projetado *pode mudar inesperadamente e isso pode causar estragos no próprio aplicativo: colunas referenciadas por ordinal (por exemplo, sqldatareader.getstring (2)) recuperam subitamente uma coluna diferente , qualquer INSERT ... SELECT *irá quebrar e assim por diante.
Remus Rusanu

2
@chaos: colocar blobs em tabelas não é realmente vai prejudicar o seu desempenho muito ... A menos que você use SELECT * ... ;-)
Dave Markle

2
Você não deve se preocupar com o desempenho até que ele cause problemas reais. E também, SELECT *não se trata de salvar poucos caracteres. É uma questão de economizar horas de tempo de depuração, porque é fácil esquecer de especificar novas colunas adicionadas.
22416 Lewis

4

Se você nomear as colunas em uma instrução SELECT, elas serão retornadas na ordem especificada e, portanto, poderão ser referenciadas com segurança pelo índice numérico. Se você usar "SELECT *", poderá receber as colunas em sequência arbitrária e, portanto, só poderá usá-las com segurança pelo nome. A menos que você saiba com antecedência o que deseja fazer com qualquer nova coluna adicionada ao banco de dados, a ação correta mais provável é ignorá-lo. Se você estiver ignorando novas colunas adicionadas ao banco de dados, não há nenhum benefício em recuperá-las.


"pode, assim, ser seguramente referenciado pelo índice numérico", mas que seria suficiente estúpido para sempre tentar fazer referência a uma coluna por índice numérico em vez do seu nome !? Esse é um anti-padrão muito pior do que usar select * em uma exibição.
precisa saber é o seguinte

@MGOwen: Usar select *e usar as colunas por índice seria horrível, mas usar select X, Y, Zou select A,B,Cpassar o leitor de dados resultante para o código que espera fazer algo com os dados nas colunas 0, 1 e 2 parece uma maneira perfeitamente razoável de permita que o mesmo código atue sobre X, Y, Z ou A, B, C. Observe que os índices das colunas dependerão da localização na instrução SELECT, e não da ordem no banco de dados.
Supercat

3

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


1
Então, como nomear as colunas ajuda? No SQL Server, as consultas existentes, incorporadas ao código ou aos SPs, não reclamam até que sejam executadas, mesmo que você tenha nomeado as colunas. Os novos falharão quando você os testar, mas você precisará passar bastante tempo procurando por SPs afetados por alterações na tabela. A que tipo de situações você está se referindo que seriam capturadas no momento do design?
ChrisA

3

Se você realmente deseja todas as colunas, não vi diferença de desempenho entre selecionar (*) e nomear as colunas. O driver para nomear as colunas pode ser simplesmente explícito sobre quais colunas você espera ver no seu código.

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


3

Pense nisso como reduzir 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 maior probabilidade de travar seu aplicativo.


3

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

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

Esse é o tipo de erro difícil de encontrar. Você faz uma alteração em um local e ela explode em outro local que não usa os novos dados. Pode até ser uma consulta usada com menos frequência, e leva um tempo até que alguém a use, o que torna ainda mais difícil conectar o erro à alteração.

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



2

Referência retirada deste artigo.

Nunca vá com "SELECT *",

Encontrei apenas um motivo para usar "SELECT *"

Se você possui requisitos especiais e criou um ambiente dinâmico ao adicionar ou excluir uma coluna, manipule automaticamente pelo código do aplicativo. Nesse caso especial, você não precisa alterar o código do aplicativo e do banco de dados e isso afetará automaticamente o ambiente de produção. Nesse caso, você pode usar "SELECT *".


1

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

Dessa forma, você pode adicionar campos às suas tabelas (mesmo no meio delas) por vários motivos, sem quebrar o código de acesso sql em todo o aplicativo.


1

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

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

Sim, se você adicionar uma coluna extra, seria mais rápido, mas na maioria dos casos, você precisará / precisará alterar seu código usando a consulta para aceitar as novas colunas de qualquer maneira, e existe o potencial de obter as que você não usa ' t quer / espera pode causar problemas. Por exemplo, se você pegar todas as colunas, confiar na ordem em um loop para atribuir variáveis ​​e adicionar uma, ou se as ordens da coluna mudarem (se isso acontecer ao restaurar a partir de um backup), isso poderá prejudicar tudo.

Esse também é o mesmo tipo de raciocínio porque, se você estiver fazendo um, INSERTdeve sempre especificar as colunas.


1

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

Como em todas as coisas, há benefícios e custos. Penso que parte da equação benefício x custo é exatamente o controle que 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), então não havia muito risco de alguém colocar um campo BLOB enorme em uma tabela.


1

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

SELECT * expõe seu sistema a alterações inesperadas de desempenho e funcionalidade no caso em que o esquema do banco de dados é alterado porque você adiciona novas colunas à tabela, embora seu código não esteja preparado para usar ou apresentar esses novos dados.


1

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

Por exemplo: BigQuery :

Preços de consulta

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

e Controle de projeção - Evite SELECT * :

Prática recomendada: Controlar a projeção - consulte apenas as colunas necessárias.

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

Usar SELECT * é a maneira mais cara de consultar dados. Quando você usa SELECT *, o BigQuery faz uma varredura completa de todas as colunas da tabela.


0

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 ou recursos do fornecedor; ou seja, ... armazenamento em cache, recursos na memória 4) tipos de dados 5) tamanho da tabela 6) frequência da consulta 7) cargas de trabalho relacionadas se o recurso for compartilhado 8) teste

A) Os requisitos variam. Se o hardware não 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 suportar visualizações, você poderá criar uma exibição indexada (?) Dos dados específicos com as colunas nomeadas específicas (vs. selecione '*'). Revise periodicamente seus dados e esquema para garantir que você nunca tenha a síndrome "Garbage-in" -> "Garbage-out".

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

1) Indexação: A seleção * executará um scan de tabelas. Dependendo de vários fatores, isso pode envolver uma busca e / ou contenção de disco com outras consultas. Se a tabela for multiuso, verifique se todas as consultas têm bom desempenho e são executadas abaixo do tempo previsto. 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. Os tempos de E / S e a carga no sistema / CPU variam.

3) O DBA pode ajustar o banco de dados / tabelas para obter melhor desempenho? Assumindo por qualquer motivo, as equipes decidiram que o '*' selecionado é a melhor solução para o problema; o banco de dados 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 é exibido para gerar 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 os 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. selecione '*' é comum em pequenas tabelas. Eles geralmente cabem na memória e os tempos de resposta são rápidos. Mais uma vez ... revise seus requisitos. Planeje a fluência de recursos; sempre planeje as atuais e possíveis necessidades futuras.

6) Frequência de consultas / consultas. Esteja ciente de outras cargas de trabalho no sistema. Se essa consulta for disparada a cada segundo, e a tabela for pequena. O conjunto de resultados pode ser projetado para permanecer no cache / memória. No entanto, se a consulta for um processo em lote frequente 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? Esta é 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 executar 10 mil consultas por segundo versus 5 a 10 consultas por segundo. O servidor de banco de dados é dedicado, ou executa outros aplicativos, monitorando a execução no recurso compartilhado. Alguns aplicativos / idiomas; Os sistemas operacionais consumirão 100% da memória, causando vários sintomas / problemas.

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.