selecionar * vs selecionar coluna

124

Se eu precisar de apenas 2/3 colunas e consultar em SELECT *vez de fornecê-las na consulta selecionada, há alguma degradação no desempenho em relação a mais / menos E / S ou memória?

A sobrecarga da rede pode estar presente se eu selecionar * sem necessidade.

Mas em uma operação de seleção, o mecanismo de banco de dados sempre extrai tupla atômica do disco ou apenas as colunas solicitadas na operação de seleção?

Se ele sempre puxa uma tupla, a sobrecarga de E / S é a mesma.

Ao mesmo tempo, pode haver um consumo de memória para remover as colunas solicitadas da tupla, se ele puxar uma tupla.

Portanto, se for esse o caso, selecione someColumn terá mais sobrecarga de memória do que a seleção *

Neel Basu
fonte
Você está perguntando sobre um RDBMS específico? É possível que a maneira como as SELECTconsultas sejam executadas / processadas seja diferente de banco de dados para banco de dados.
Lèse majesté
10
Além disso, no PostgreSQL, se você disser CREATE VIEW foo_view AS SELECT * FROM foo;, adicione colunas à tabela foo mais tarde, essas colunas não aparecerão automaticamente no foo_view conforme o esperado. Em outras palavras, o *nesse contexto se expande apenas uma vez (no momento da criação da exibição), não por SELECT. Por causa das complicações decorrentes da ALTER TABLE, eu diria que (na prática) *é considerado prejudicial.
Joey Adams
@ JoeyAdams - não apenas o PostgresQL, esse também é o comportamento do Oracle.
APC
1
Pôneis @ OMG: Eu não estava ciente de post semelhante. No entanto, esses não são realmente simuladores. @ Lêse majesté: Estou falando sobre RDBMS genérico. não sobre nenhum fornecedor específico @ Joey Adams: Hmm, eu sei que * é inseguro. só quero discutir as questões de desempenho relacionadas.
Neel Basu
3
possível duplicata de Por que SELECT * é considerado prejudicial?
Aaron Bertrand

Respostas:

31

Ele sempre puxa uma tupla (exceto nos casos em que a tabela foi verticalmente segmentada - dividida em partes de colunas); portanto, para responder à pergunta que você fez, não importa da perspectiva de desempenho. No entanto, por muitos outros motivos, (abaixo) você sempre deve selecionar especificamente as colunas que deseja, pelo nome.

Ele sempre puxa uma tupla, porque (em todos os fornecedores que RDBMS eu conheço), a estrutura subjacente de armazenamento em disco para tudo (incluindo dados da tabela) é baseada em páginas de E / S definidas (no SQL Server, por exemplo, cada página é 8 kilobytes). E toda leitura / gravação de E / S é feita por página. Ou seja, toda gravação ou leitura é uma página completa de dados.

Devido a essa restrição estrutural subjacente, uma consequência é que cada linha de dados em um banco de dados deve estar sempre em uma e apenas uma página. Ele não pode abranger várias páginas de dados (exceto para coisas especiais, como blobs, onde os dados reais do blob são armazenados em partes de página separadas e a coluna da linha da tabela real recebe apenas um ponteiro ...). Mas essas exceções são apenas isso, exceções, e geralmente não se aplicam, exceto em casos especiais (para tipos especiais de dados ou certas otimizações para circunstâncias especiais).
Mesmo nesses casos especiais, geralmente, a própria linha da tabela de dados (que contém o ponteiro para os dados reais do Blob, ou o que for), ele deve ser armazenado em uma única página de E / S.

EXCEÇÃO. O único local em que Select *está OK é na subconsulta após uma cláusula Existsou Not Existspredicado, como em:

   Select colA, colB
   From table1 t1
   Where Exists (Select * From Table2
                 Where column = t1.colA)

EDIT: Para abordar o comentário de Mike Sherer, Sim, é verdade, tanto tecnicamente, com um pouco de definição para o seu caso especial e esteticamente. Primeiro, mesmo quando o conjunto de colunas solicitadas é um subconjunto daquelas armazenadas em algum índice, o processador de consultas deve buscar todas as colunas armazenadas nesse índice, não apenas as solicitadas, pelos mesmos motivos - TODAS as E / S devem ser feitas em páginas e dados de índice são armazenados nas páginas IO, assim como os dados da tabela. Portanto, se você definir "tupla" para uma página de índice como o conjunto de colunas armazenadas no índice, a instrução ainda será verdadeira.
e a afirmação é verdadeira esteticamente porque o ponto é que ele busca dados com base no que é armazenado na página de E / S, não no que você solicita, e isso é verdade se você está acessando a Página de E / S da tabela base ou um índice Página de E / S.

Por outras razões para não usar Select *, consulte Por que é SELECT *considerado prejudicial? :

Charles Bretana
fonte
"Ele sempre puxa uma tupla", você tem certeza? Hmm Ok, então eu estava certa. se for esse o caso select *, terá menos sobrecarga de memória do que a select columnmesma sobrecarga de E / S. Então, se deixarmos a sobrecarga da rede. select *se menos sobrecarga do que a deselect column
Neel Basu
10
Isso não é verdade. Um exemplo muito importante é quando você deseja apenas o valor de uma coluna indexada no MySQL (por exemplo, apenas para verificar a existência de linhas), e você estiver usando o mecanismo de armazenamento MyISAM, os dados serão capturados Arquivo MYI, que pode estar na memória e nem mesmo ir para o disco!
5118 Mike Sherov
Sim, se o conjunto de tupla solicitado estiver na memória, não haverá E / S, mas esse é o caso especial. Então, qual é o verão? Se eu selecionar alguma coluna indexada, a tupla inteira não será lida? caso contrário, toda a tupla é lida?
Neel Basu
Não sei exatamente como o MySql faz o cache, mas no SQL Server e no Oracle, mesmo quando os dados estão no cache da memória, eles ainda os acessam usando o mesmo estruturador de página do que quando acessam a partir do disco. o que significa que seria necessária uma E / S de memória por página de dados ... exatamente a mesma do disco. (exceto as E / Ss de memória, são muito mais rápidas que as E / S de disco, é claro). De fato, esse é um objetivo do design do cache, para tornar o processo de acesso totalmente independente da localização dos dados.
Charles Bretana
2
Você pode explicar mais "por muitas outras razões"? Porque isso não estava claro para mim. Se o desempenho não importa, por que se preocupar em solicitar nomes de colunas?
Dennis
111

Há vários motivos para você nunca (nunca) usar SELECT *no código de produção:

  • como você não está dando dicas ao seu banco de dados sobre o que deseja, primeiro será necessário verificar a definição da tabela para determinar as colunas nessa tabela. Essa pesquisa custará algum tempo - não muito em uma única consulta - mas aumenta com o tempo.

  • se você precisar de apenas 2/3 das colunas, estará selecionando 1/3 de dados em excesso que precisam ser recuperados do disco e enviados pela rede

  • se você começar a confiar em certos aspectos dos dados, por exemplo, a ordem das colunas retornadas, poderá receber uma surpresa desagradável assim que a tabela for reorganizada e novas colunas forem adicionadas (ou removidas as existentes)

  • no SQL Server (não tenho certeza sobre outros bancos de dados), se você precisar de um subconjunto de colunas, sempre haverá uma chance de um índice não clusterizado estar cobrindo essa solicitação (contém todas as colunas necessárias). Com um SELECT *, você está desistindo dessa possibilidade desde o início. Nesse caso específico, os dados seriam recuperados das páginas de índice (se elas contiverem todas as colunas necessárias) e, portanto, a E / S do disco e a sobrecarga de memória seriam muito menos em comparação com a realização de uma SELECT *....consulta.

Sim, é necessário um pouco mais de digitação inicialmente (ferramentas como o SQL Prompt para SQL Server até o ajudarão lá) - mas esse é realmente um caso em que há uma regra sem exceção: nunca use SELECT * no seu código de produção. SEMPRE.

marc_s
fonte
13
Embora concorde com você na prática, você certamente está correto em todos os casos ao buscar dados da coluna da tabela, conforme esta pergunta é abordada), mas a ênfase em EVER me leva a salientar que essas regras não são gerais para TODAS as consultas SQL. especificamente, é usado em uma subconsulta após um predicado EXISTS, (como em Where Exists (Select * From ...) o uso de Select *certamente não é um problema e, em alguns círculos, é considerado uma prática recomendada.
Charles Bretana
3
@Charles Bretana: sim, o IF EXISTS(SELECT *...é um caso especial - uma vez lá, nenhum dado é realmente recuperado, mas é apenas um cheque de existência, o SELECT * não é um problema lá ...
marc_s
1
E se eu estiver desenvolvendo uma API que permita recuperar dados de uma das minhas tabelas. Como não sei em quais dados o usuário está interessado, suponho que SELECT * seja aceitável?
Simon Bengtsson
1
@ SimonBengtsson: Eu ainda argumentaria contra isso - suponha que você tenha alguns dados "administrativos" em colunas específicas da sua tabela que não deseja expor para o cliente? Eu sempre especificar explicitamente uma lista de colunas para buscar
marc_s
1
Isso é verdade. E quando consultar uma visualização que foi especificamente configurada para ser usada com a API?
Simon Bengtsson
21

Você deve sempre apenas selectas colunas que realmente precisa. Nunca é menos eficiente selecionar menos em vez de mais e você também enfrenta menos efeitos colaterais inesperados - como acessar suas colunas de resultados no lado do cliente por índice e, depois, tornar esses índices incorretos adicionando uma nova coluna à tabela.

[editar]: Significou acessar. Cérebro estúpido ainda acordando.

Donnie
fonte
3
+1 para um caso de ponta que acredito que muitos não vão pensar à primeira vista - índices no lado do cliente e colunas adicionadas / alteradas.
Tomas Aschan
1
Sim, mas o uso de índices numéricos para colunas é comum? Sempre acessei os dados da coluna usando chaves de seqüência de caracteres ou nomes de propriedades se estiver usando o ORM.
Lèse majesté
11
vi isso há muito tempo, o programador júnior selecionou * de uma tabela e fez suposições sobre a ordem das colunas; todo o código dele quebrou assim que alguém mudou a mesa. Que divertido nós tivemos.
Paul McKenzie
7
Provavelmente, é uma má idéia usar a ordem das colunas em geral apenas para facilitar a leitura do código, duplamente ruim de usar SELECT *.
Lèse majesté
2
Uau, acessar colunas por índice no código do cliente parece ser uma fenomenalmente má idéia. Para esse assunto, confiar na ordem em que as colunas aparecem em um conjunto de resultados de alguma forma me parece muito sujo.
Matt Peterson
7

A menos que você esteja armazenando grandes bolhas, o desempenho não é uma preocupação. O grande motivo para não usar SELECT * é que, se você estiver usando linhas retornadas como tuplas, as colunas retornarão na ordem que o esquema especificar, e se isso mudar, você precisará corrigir todo o seu código.

Por outro lado, se você usa o acesso no estilo de dicionário, não importa em que ordem as colunas retornam, porque você sempre as acessa pelo nome.

gxti
fonte
6

Isso imediatamente me faz pensar em uma tabela que eu estava usando que continha uma coluna do tipo blob; geralmente continha uma imagem JPEG, com alguns Mbs de tamanho.

Escusado será dizer que eu não fiz SELECTessa coluna, a menos que eu realmente precisava. Ter esses dados flutuando - especialmente quando selecionei várias linhas - era apenas um aborrecimento.

No entanto, admitirei que, de outra forma, normalmente consulta todas as colunas em uma tabela.

Richard JP Le Guen
fonte
20
As colunas LOB são sempre o meu exemplo favorito dos perigos de SELECT *. Então, eu ia votar em você até ler o terceiro parágrafo. Tsk, tsk. O que acontece se algum outro desenvolvedor adicionar um BLOB a uma tabela que atualmente não possui essa coluna?
APC
1
@APC, gostaria de poder votar mais seu comentário. Pense no seu pobre colega de trabalho que apenas deseja adicionar uma coluna sem causar um enorme colapso no desempenho! Pense em como ficarão zangados quando descobrirem, depois de algumas horas, sua aparência inocente selecionar *.
Mike Sherov
1
@ user256007, sim, mesmo sem BLOB ... BLOB apenas ilustra o exemplo extremo. Verifique minha resposta a Charles, há momentos em que a seleção de colunas específicas pode permitir que você pegue os dados da memória sem precisar ir para o disco!
Mike Sherov
1
@ Richard, acho que eles são ótimos para otimizar o desempenho do banco de dados não é sua principal preocupação, que é 99% do tempo. Como na maioria das estruturas, elas tendem a generalizar as coisas para permitir um desenvolvimento mais rápido, sacrificando o desempenho puro. Como Knuth disse: "A otimização prematura é a raiz de todo mal". Quando você chega ao ponto em que precisa se preocupar com o desempenho de colunas selecionadas versus seleção *, (pergunte ao Twitter sobre o RoR), você pode se preocupar com isso e otimizá-lo. Se a estrutura não for suficientemente robusta para suportar isso, diria que você está usando a estrutura errada.
Mike Sherov
1
@ user256007 - a regra geral é "não use SELECT * '. A resposta de marc_s tem toda a razão de ser esse o caso."
APC
6

Durante uma seleção SQL, o banco de dados sempre se refere aos metadados da tabela, independentemente de ser SELECT * para SELECT a, b, c ... Por que? Porque é aí que estão as informações sobre a estrutura e o layout da tabela no sistema.

Ele precisa ler essas informações por dois motivos. Um, simplesmente compilar a declaração. Ele precisa garantir que você especifique uma tabela existente, no mínimo. Além disso, a estrutura do banco de dados pode ter sido alterada desde a última vez que uma instrução foi executada.

Agora, obviamente, os metadados do banco de dados são armazenados em cache no sistema, mas ainda é o processamento que precisa ser feito.

Em seguida, os metadados são usados ​​para gerar o plano de consulta. Isso acontece sempre que uma declaração é compilada também. Novamente, isso é executado nos metadados armazenados em cache, mas sempre é feito.

O único momento em que esse processamento não é concluído é quando o banco de dados está usando uma consulta pré-compilada ou armazenou em cache uma consulta anterior. Este é o argumento para usar parâmetros de ligação em vez de SQL literal. "SELECT * FROM TABLE WHERE key = 1" é uma consulta diferente de "SELECT * FROM TABLE WHERE key =?" e o "1" é vinculado à chamada.

Os bancos de dados dependem muito do cache da página para que funcione. Muitos bancos de dados modernos são pequenos o suficiente para caber completamente na memória (ou, talvez eu deva dizer, a memória moderna é grande o suficiente para caber muitos bancos de dados). Então, o seu custo de E / S principal no back-end é o log e as descargas de página.

No entanto, se você ainda estiver pressionando o disco para o seu banco de dados, uma otimização primária feita por muitos sistemas é confiar nos dados nos índices, e não nas próprias tabelas.

Se você tem:

CREATE TABLE customer (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    city VARCHAR(30),
    state VARCHAR(30),
    zip VARCHAR(10));

CREATE INDEX k1_customer ON customer(id, name);

Então, se você selecionar "SELECT id, nome FROM customer WHERE id = 1", é muito provável que o banco de dados extraia esses dados do índice, e não das tabelas.

Por quê? Provavelmente, ele usará o índice de qualquer maneira para satisfazer a consulta (versus uma verificação de tabela) e, embora 'name' não seja usado na cláusula where, esse índice ainda será a melhor opção para a consulta.

Agora, o banco de dados tem todos os dados necessários para satisfazer a consulta, portanto, não há motivo para acessar as páginas da tabela. O uso do índice resulta em menos tráfego de disco, pois você tem uma densidade mais alta de linhas no índice versus a tabela em geral.

Esta é uma explicação manual de uma técnica de otimização específica usada por alguns bancos de dados. Muitos têm várias técnicas de otimização e ajuste.

No final, SELECT * é útil para consultas dinâmicas que você precisa digitar manualmente, eu nunca o usaria para "código real". A identificação de colunas individuais fornece ao banco de dados mais informações que ele pode usar para otimizar a consulta e oferece um controle melhor no seu código contra alterações de esquema etc.

Will Hartung
fonte
Will, diminuí a votação da sua resposta, apenas porque você usa NOT NULL junto com a PRIMARY KEY. Existe uma boa razão para você escrever dessa maneira?
Learner
4

Acho que não há uma resposta exata para sua pergunta, porque você está pensando no desempenho e na facilidade de manter seus aplicativos. Select columné mais performático select *, mas se você estiver desenvolvendo um sistema de objetos orientado, gostará de usar object.propertiese poderá precisar de propriedades em qualquer parte dos aplicativos; precisará escrever mais métodos para obter propriedades em situações especiais, se não o fizer. use select *e preencha todas as propriedades. Seus aplicativos precisam ter um bom desempenho usando select *e, em alguns casos, você precisará usar a coluna select para melhorar o desempenho. Então você terá o melhor de dois mundos, facilidade para escrever e manter aplicativos e desempenho quando precisar de desempenho.

M.Torres
fonte
4

A resposta aceita aqui está errada. Me deparei com isso quando outra pergunta foi fechada como uma duplicata disso (enquanto eu ainda estava escrevendo minha resposta - grr -, portanto, o SQL abaixo faz referência à outra pergunta).

Você sempre deve usar o atributo SELECT, atributo .... NOT SELECT *

É principalmente para problemas de desempenho.

SELECT nome dos usuários WHERE name = 'John';

Não é um exemplo muito útil. Considere em vez disso:

SELECT telephone FROM users WHERE name='John';

Se houver um índice ativado (nome, telefone), a consulta poderá ser resolvida sem a necessidade de procurar os valores relevantes da tabela - há uma cobertura índice de .

Além disso, suponha que a tabela possua um BLOB contendo uma imagem do usuário, um CV carregado e uma planilha ... usando SELECT * reunirá todas essas informações nos buffers do DBMS (forçando outras informações úteis do cache). Em seguida, tudo será enviado ao cliente usando o tempo de funcionamento na rede e a memória no cliente para dados redundantes.

Também pode causar problemas funcionais se o cliente recuperar os dados como uma matriz enumerada (como mysql_fetch_array do PHP ($ x, MYSQL_NUM)). Talvez quando o código foi escrito 'phone' foi a terceira coluna a ser retornada por SELECT *, mas alguém aparece e decide adicionar um endereço de email à tabela, posicionado antes de 'telephone'. O campo desejado agora é deslocado para a quarta coluna.

symcbean
fonte
2

Existem razões para fazer as coisas de qualquer maneira. Eu uso muito o SELECT * no PostgreSQL porque há muitas coisas que você pode fazer com o SELECT * no PostgreSQL que você não pode fazer com uma lista de colunas explícita, principalmente quando em procedimentos armazenados. Da mesma forma no Informix, SELECT * em uma árvore de tabela herdada pode fornecer linhas irregulares, enquanto uma lista explícita de colunas não pode, porque também são retornadas colunas adicionais nas tabelas filho.

A principal razão pela qual faço isso no PostgreSQL é que ele garante um tipo bem formado específico para uma tabela. Isso me permite pegar os resultados e usá-los como o tipo de tabela no PostgreSQL. Isso também permite muito mais opções na consulta do que uma lista rígida de colunas permitiria.

Por outro lado, uma lista rígida de colunas fornece uma verificação no nível do aplicativo, de que os esquemas de banco de dados não foram alterados de determinadas maneiras e isso pode ser útil. (Eu faço essas verificações em outro nível.)

Quanto ao desempenho, costumo usar VIEWs e procedimentos armazenados retornando tipos (e depois uma lista de colunas dentro do procedimento armazenado). Isso me dá controle sobre quais tipos são retornados.

Mas lembre-se de que estou usando SELECT * geralmente em uma camada de abstração em vez de em tabelas base.

Chris Travers
fonte
2

Referência retirada deste artigo:

Sem SELECT *: quando você estiver usando "SELECT *" naquele momento, estará selecionando mais colunas do banco de dados e parte dessa coluna poderá não ser usada pelo seu aplicativo. Isso criará custos e cargas extras no sistema de banco de dados e mais dados serão transportados pela rede.

Com 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 *".

Anvesh
fonte
0

Apenas para adicionar uma nuance à discussão que não vejo aqui: Em termos de E / S, se você estiver usando um banco de dados com armazenamento orientado a colunas poderá fazer MUITO menos E / S se consultar apenas determinadas colunas. À medida que mudamos para SSDs, os benefícios podem ser um pouco menores do que o armazenamento orientado a linhas, mas há: a) apenas a leitura dos blocos que contêm colunas importantes para você; b) compactação, que geralmente reduz bastante o tamanho dos dados no disco e, portanto, o volume de dados lidos do disco.

Se você não está familiarizado com o armazenamento orientado a colunas, uma implementação do Postgres vem do Citus Data, outra é Greenplum, outra Paraccel, outra (em termos gerais) é o Amazon Redshift. Para o MySQL, existe o Infobright, o InfiniDB agora quase extinto. Outras ofertas comerciais incluem Vertica da HP, Sybase IQ, Teradata ...

Carnot Antonio Romero
fonte
-1
select * from table1 INTERSECT  select * from table2

igual

select distinct t1 from table1 where Exists (select t2 from table2 where table1.t1 = t2 )
mehdi sadeghi
fonte
Poderia, por favor formatar seu código, destacando-o e pressionando Ctrl + K
WhatsThePoint