Qual é o motivo para não usar o select *?

136

Vi várias pessoas afirmarem que você deve nomear especificamente cada coluna que deseja na sua consulta de seleção.

Supondo que eu usarei todas as colunas de qualquer maneira, por que não usaria SELECT *?

Mesmo considerando a pergunta * consulta SQL - selecione * da visualização ou selecione col1, col2, ... colN da visualização *, não acho que seja uma duplicata exata, pois estou abordando o problema de uma perspectiva um pouco diferente.

Um de nossos princípios é não otimizar antes da hora. Com isso em mente, parece que o uso SELECT *deve ser o método preferido até que se prove ser um problema de recurso ou o esquema esteja praticamente definido. O que, como sabemos, não ocorrerá até que o desenvolvimento esteja completo.

Dito isto, existe um problema primordial para não usar SELECT *?

Eu não
fonte

Respostas:

168

A essência da citação de não otimizar prematuramente é optar por um código simples e direto e, em seguida, usar um criador de perfil para apontar os pontos de acesso, que você poderá otimizar para ser eficiente.

Quando você usa select *, torna-se impossível criar um perfil, portanto, não está escrevendo um código claro e direto e vai contra o espírito da citação. select *é um anti-padrão.


Portanto, selecionar colunas não é uma otimização prematura. Algumas coisas em cima da minha cabeça ....

  1. Se você especificar colunas em uma instrução SQL, o mecanismo de execução SQL causará erro se essa coluna for removida da tabela e a consulta for executada.
  2. Você pode verificar com mais facilidade o código onde essa coluna está sendo usada.
  3. Você sempre deve escrever consultas para trazer de volta a menor quantidade de informações.
  4. Como outros mencionam, se você usa acesso à coluna ordinal, nunca deve usar select *
  5. Se sua instrução SQL ingressar em tabelas, selecione * fornece todas as colunas de todas as tabelas na associação

O corolário é que, usando select *...

  1. As colunas usadas pelo aplicativo são opacas
  2. Os DBAs e seus criadores de perfil de consulta não conseguem ajudar no desempenho ruim do aplicativo
  3. O código é mais quebradiço quando ocorrem alterações
  4. Seu banco de dados e sua rede estão sofrendo porque estão recuperando muitos dados (E / S)
  5. As otimizações do mecanismo de banco de dados são mínimas, pois você recupera todos os dados independentemente (lógico).

Escrever o SQL correto é tão fácil quanto escrever Select *. Portanto, a pessoa preguiçosa real escreve SQL apropriado porque não deseja revisitar o código e tenta se lembrar do que estava fazendo quando o fez. Eles não querem explicar aos DBAs sobre todo o código. Eles não querem explicar aos seus clientes por que o aplicativo é executado como um cachorro.

Robert Paulson
fonte
2
Na sua primeira seção, o ponto 5 deve ler "select * fornece todas as colunas de todas as tabelas na junção". Na sua segunda seção, os pontos 2 e 5 não são necessariamente verdadeiros e não devem ser listados como motivos para não usar "selecionar *".
jimmyorr
1
@uglysmurf - obrigado pela correção, mas no que diz respeito aos 2 e 5 - embora eles possam não ser necessariamente verdade para todos os bancos de dados / dba's em todos os casos, eu sinto que eles são importantes e válidos para a maioria dos casos e os deixarão como estão. Usar 'select *' nunca facilitou o trabalho de um dba.
9119 Robert Paulson
11
Eu diria que o número 3 (código quebradiço) não é realmente verdade. Dependendo da implementação, o Select * pode torná-lo MENOS quebradiço, mas não vejo como poderia ser mais.
31410 JohnFx
2
@ JohnFx, acho que você define frágil de forma diferente. Frágil é normalmente definido como 'quebra facilmente'. Ter dependências desconhecidas ou difíceis de encontrar, porque cada pedaço de código usará colunas diferentes significa que não é possível alterar facilmente nada no nível de dados sem regressão completa ... o que parece frágil.
9119 Robert Paulson
9
@mavnn, fragilidade, tenho medo de que isso se torne uma questão semântica na minha escolha da palavra quebradiço. Minha última palavra é dizer que faz pouca diferença de qualquer maneira. O único cenário é renomeado / removido de colunas. Você está apenas mudando a interrupção de quando o sql é executado (explícito) versus a interrupção quando os resultados são consumidos. A maneira pela qual o resultado da consulta é consumido pode variar e o código pode falhar silenciosamente ou não, mas o mecanismo de execução do sql definitivamente falhará com o sql inválido. Então, selecione * o ajudou? Falha explícita da IMO mais próxima do DB para um problema de DB é melhor. Thx
Robert Paulson
42

Se o seu código depender de as colunas estarem em uma ordem específica, ele será interrompido quando houver alterações na tabela. Além disso, você pode estar buscando demais na tabela ao selecionar *, especialmente se houver um campo binário na tabela.

Só porque você está usando todas as colunas agora, isso não significa que alguém não irá adicionar uma coluna extra à tabela.

Ele também adiciona sobrecarga ao cache de execução do plano, pois precisa buscar os metadados sobre a tabela para saber em que colunas estão *.

Prumo
fonte
4
Boa resposta, mas eu mudaria o "código quebrará" para "o código PODE quebrar". Esse é o verdadeiro problema aqui, o uso "select *" não produz SEMPRE uma mudança de última hora. E quando o intervalo acontece, geralmente é altamente dissociado do uso que acaba sendo quebrado.
BQ.
4
Se alguém estiver referenciando colunas normalmente em seu código, estará com problemas, independentemente de usar SELECT * ou não. A sobrecarga de execução do plano é trivial e não importa de qualquer maneira, uma vez que o plano é armazenado em cache.
283 MusiGenesis
1
Em seguida, o erro do programador consiste em escrever um código que depende da sequência das colunas. Você nunca precisa fazer isso.
23468 dkretz
1
@doofledorfer - nunca diga nunca. É mais rápido acessar colunas ordinais e às vezes é prático. É um erro maior usar o select * do que o acesso ordinal.
Robert Paulson
23

Um dos principais motivos é que, se você adicionar / remover colunas da sua tabela, qualquer consulta / procedimento que esteja fazendo uma chamada SELECT * agora estará recebendo mais ou menos colunas de dados do que o esperado.

ahockley
fonte
3
Você nunca deve escrever um código que dependa do número de colunas retornadas.
23468 dkretz
4
Mas todo mundo está escrevendo um código que exige que os programadores saibam quais dados estão retornando. Você não pode pressionar Ctrl + F no nome da sua coluna se ela estiver oculta em um SELECT *.
Lotus Notes
17
  1. De uma maneira indireta, você está violando a regra da modularidade sobre o uso de digitação estrita sempre que possível. Explícito é quase universalmente melhor.

  2. Mesmo que agora você precise de todas as colunas da tabela, mais poderá ser adicionado posteriormente, que será retirado toda vez que você executar a consulta e poderá prejudicar o desempenho. Dói o desempenho porque

    • Você está puxando mais dados pelo fio; e
    • Porque você pode derrotar a capacidade do otimizador de extrair os dados diretamente do índice (para consultas em colunas que fazem parte de um índice.) Em vez de fazer uma pesquisa na própria tabela

Quando usar, selecione *

Quando você NECESSITAM explicitamente de todas as colunas da tabela, em vez de precisar de todas as colunas da tabela EXISTENTES NO MOMENTO EM QUE VOCÊ ESCREVEU A CONSULTA. Por exemplo, se estivesse escrevendo um aplicativo de gerenciamento de banco de dados que precisava exibir todo o conteúdo da tabela (o que quer que fosse), você poderia usar essa abordagem.

JohnFx
fonte
1
Outro momento para usar SELECT *seria quando você estiver fazendo consultas de teste usando o cliente db.
Cdmckay
Isso parece uma exceção estranha, dado o contexto da pergunta. Além de salvar algumas digitações, qual é a vantagem de fazer isso para consultas de teste?
31410 JohnFx
Também SELECT * FROM (tabela SELECT a, b, c FROM) está OK.
Km19plan #
12

Há algumas razões:

  1. Se o número de colunas em um banco de dados for alterado e seu aplicativo esperar que haja um determinado número ...
  2. Se a ordem das colunas em um banco de dados for alterada e seu aplicativo esperar que elas estejam em uma determinada ordem ...
  3. Sobrecarga de memória. 8 colunas INTEGER desnecessárias adicionariam 32 bytes de memória desperdiçada. Isso não parece muito, mas isso é para cada consulta e INTEGER é um dos tipos de colunas pequenas ... é mais provável que colunas extras sejam colunas VARCHAR ou TEXT, o que aumenta mais rapidamente.
  4. Sobrecarga de rede. Relacionado à sobrecarga de memória: se eu emitir 30.000 consultas e tiver 8 colunas INTEGER desnecessárias, desperdicei 960kB de largura de banda. As colunas VARCHAR e TEXT provavelmente serão consideravelmente maiores.

Nota: eu escolhi INTEGER no exemplo acima porque eles têm um tamanho fixo de 4 bytes.

Powerlord
fonte
1 e 2 seria um cheiro de código e 3 e 4 som como optimização prematura
NikkyD
7

Se seu aplicativo obtiver dados com SELECT * e a estrutura da tabela no banco de dados for alterada (digamos, uma coluna foi removida), seu aplicativo falhará em todos os locais aos quais você referenciar o campo ausente. Se você incluir todas as colunas na sua consulta, seu aplicativo entrará no (espero) um lugar onde você obtém os dados inicialmente, facilitando a correção.

Dito isto, há várias situações em que SELECT * é desejável. Uma é uma situação que encontro o tempo todo, em que preciso replicar uma tabela inteira em outro banco de dados (como o SQL Server para o DB2, por exemplo). Outro é um aplicativo escrito para exibir tabelas genericamente (ou seja, sem o conhecimento de qualquer tabela específica).

MusiGenesis
fonte
A pergunta não é 'é selecionar * sempre desejável'; portanto, a segunda parte da sua resposta é irrelevante. A pergunta afirma que o uso de 'select *' deve ser preferível, o que, obviamente, é uma besteira completa.
Robert Paulson
Sim, minha segunda parte é irrelevante. OQ mudou a pergunta para o estado SELECT * é preferível, e sim, isso é meio bollocksy.
26320 MusiGenesis
Ah, sim, desculpe - a pergunta mudou de direção após a sua resposta.
Robert Paulson
Tudo bem. Até Mozart era um editor ( stackoverflow.com/questions/292682/… ). Meu post original sugeriu que o uso do SELECT * levou ao canibalismo. :)
MusiGenesis
3

Na verdade, notei um comportamento estranho quando usei os select *modos de exibição no SQL Server 2005.

Execute a seguinte consulta e você verá o que quero dizer.

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','c1'
union all select 'a2','b2','c2'
union all select 'a3','b3','c3'

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]'))
DROP VIEW [dbo].[vStartest]
go
create view dbo.vStartest as
select * from dbo.starTest
go

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]'))
DROP VIEW [dbo].[vExplicittest]
go
create view dbo.[vExplicittest] as
select a,b,c from dbo.starTest
go


select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicitTest

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [D] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','d1','c1'
union all select 'a2','b2','d2','c2'
union all select 'a3','b3','d3','c3'

select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicittest

Compare os resultados das duas últimas instruções de seleção. Acredito que o que você verá é o resultado do Select * referenciando colunas por índice, em vez de nome.

Se você reconstruir a exibição, ela funcionará bem novamente.

EDITAR

Eu adicionei uma pergunta separada, * "selecione * da tabela" vs "selecione colA, colB, etc. da tabela" comportamento interessante no SQL Server 2005 * para analisar esse comportamento com mais detalhes.

Kristof
fonte
2

Você pode juntar duas tabelas e usar a coluna A da segunda tabela. Se você adicionar posteriormente a coluna A à primeira tabela (com o mesmo nome, mas com um significado diferente), provavelmente obterá os valores da primeira tabela e não da segunda como anteriormente. Isso não acontecerá se você especificar explicitamente as colunas que deseja selecionar.

É claro que especificar as colunas às vezes também causa bugs se você esquecer de adicionar as novas colunas a cada cláusula de seleção. Se a nova coluna não for necessária toda vez que a consulta for executada, poderá levar algum tempo até que o erro seja notado.

Kaniu
fonte
2

Entendo aonde você está indo com relação à otimização prematura, mas isso realmente só chega a um ponto. A intenção é evitar a otimização desnecessária no início. Suas tabelas não são indexadas? Você usaria o nvarchar (4000) para armazenar um código postal?

Como outros já apontaram, existem outros pontos positivos para especificar cada coluna que você pretende usar na consulta (como a capacidade de manutenção).

Jim BG
fonte
2

Quando você está especificando colunas, também está se atando a um conjunto específico de colunas e se tornando menos flexível, fazendo Feuerstein rolar, bem, onde quer que esteja. Apenas um pensamento.

orbfish
fonte
1
Não tenho absolutamente nenhuma ideia de quem é Feuerstein. Tentei pesquisar no Google e encontrei um psicólogo, um personagem de televisão e um blogueiro para que o melhor que eu conseguisse era uma piada.
NotMe
Autor dos livros da O'Reilly sobre PL / SQL. Tente pesquisar no "feuerstein sql" em vez de apenas "feuerstein".
21412 orbfish
2

SELECT * nem sempre é mau. Ao menos na minha opinião. Eu o uso frequentemente para consultas dinâmicas que retornam uma tabela inteira, além de alguns campos computados.

Por exemplo, eu quero calcular geometrias geográficas de uma tabela "normal", que é uma tabela sem nenhum campo de geometria, mas com campos que contêm coordenadas. Eu uso o postgresql e sua extensão espacial postgis. Mas o princípio se aplica a muitos outros casos.

Um exemplo:

  • uma tabela de locais, com coordenadas armazenadas nos campos rotulados x, y, z:

    CREATE TABLE coloca (place_id número inteiro, x numérico (10, 3), y numérico (10, 3), z numérico (10, 3), descrição varchar);

  • vamos alimentá-lo com alguns valores de exemplo:

    INSERT INTO places (place_id, x, y, z, description) VALORES
    (1, 2.295, 48.863, 64, 'Paris, Place de l \' Étoile '),
    (2, 2.945, 48.858, 40,' Paris, Tour Eiffel '),
    (3, 0,373, 43,958, 90,' Condom, Cathédrale St-Pierre ');

  • Quero poder mapear o conteúdo desta tabela, usando algum cliente GIS. A maneira normal é adicionar um campo de geometria à tabela e construir a geometria, com base nas coordenadas. Mas eu preferiria obter uma consulta dinâmica: dessa maneira, quando altero as coordenadas (correções, mais precisão etc.), os objetos mapeados realmente se movem dinamicamente. Então, aqui está a consulta com o SELECT * :

    CRIAR OU SUBSTITUIR VISUALIZAR places_points AS
    SELECT *,
    GeomFromewkt ('SRID = 4326; POINT (' || x || '' || y || '' || z || ')')
    FROM places;

    Consulte o postgis, para uso da função GeomFromewkt ().

  • Aqui está o resultado:

    SELECT * FROM lugares_pontos;

place_id | x y z descrição geomfromewkt                            
---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2,295 | 48,863 | 64.000 | Paris, Place de l'Étoile | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 2.945 48,858 | 40.000 | Paris, excursão eiffel | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 0,373 | 43,958 | 90.000 | Camisinha, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 linhas)

A coluna mais à direita agora pode ser usada por qualquer programa GIS para mapear corretamente os pontos.

  • Se, no futuro, alguns campos forem adicionados à tabela: não se preocupe, basta executar novamente a mesma definição de VIEW.

Eu gostaria que a definição do VIEW pudesse ser mantida "como está", com o *, mas hélas não é o caso: é assim que é armazenado internamente pelo postgresql:

SELECT places.place_id, places.x, places.y, places.z, places.description, geomfromewkt (((((((('(SRID = 4326; POINT (' :: text || places.x) || '): : texto) || locais.y) || '' :: texto) || locais.z) || ')' :: texto) AS geomfromewkt FROM locais;

Pierre
fonte
1

Mesmo se você usar todas as colunas, mas abordar a matriz de linhas pelo índice numérico, você terá problemas se adicionar outra linha posteriormente.

Então, basicamente, é uma questão de manutenção! Se você não usar o seletor *, não precisará se preocupar com suas consultas.

markus
fonte
1

Selecionar apenas as colunas necessárias mantém o conjunto de dados na memória menor e, portanto, mantém seu aplicativo mais rápido.

Além disso, muitas ferramentas (por exemplo, procedimentos armazenados) também armazenam em cache os planos de execução de consultas. Se você adicionar ou remover mais tarde uma coluna (particularmente fácil se você estiver selecionando uma visualização), a ferramenta geralmente apresentará erros quando não obtiver os resultados esperados.

Soldarnal
fonte
1

Isso torna seu código mais ambíguo e mais difícil de manter; porque você está adicionando dados extras não utilizados ao domínio e não está claro qual você pretendeu ou não. (Também sugere que você pode não saber ou se importar.)

dkretz
fonte
1

Para responder sua pergunta diretamente: Não use "SELECT *" quando isso tornar seu código mais frágil às alterações nas tabelas subjacentes. Seu código deve ser quebrado somente quando uma alteração for feita na tabela que afeta diretamente os requisitos do seu programa.

Seu aplicativo deve tirar proveito da camada de abstração que o acesso relacional fornece.

Metro
fonte
1

Eu não uso SELECT * simplesmente porque é bom ver e saber quais campos estou recuperando.

lkessler
fonte
1

Geralmente é ruim usar 'select *' dentro das visualizações porque você será forçado a recompilar a visualização no caso de uma alteração na coluna da tabela. Alterando as colunas da tabela subjacente de uma visualização, você receberá um erro para colunas inexistentes até voltar e recompilar.

Christopher Klein
fonte
1

Tudo bem quando você está fazendo, exists(select * ...)pois nunca é expandido. Caso contrário, é realmente útil apenas ao explorar tabelas com instruções de seleção temporárias ou se você tiver um CTE definido acima e desejar todas as colunas sem digitá-las novamente.

dotjoe
fonte
1

Apenas para adicionar uma coisa que ninguém mais mencionou. Select *retorna todas as colunas, alguém pode adicionar uma coluna posteriormente que você não deseja necessariamente que os usuários possam ver, como quem atualizou os dados pela última vez ou um carimbo de data / hora ou anotações de que somente os gerentes devem ver nem todos os usuários etc.

Além disso, ao adicionar uma coluna, o impacto no código existente deve ser revisado e considerado para ver se são necessárias alterações com base nas informações armazenadas na coluna. Ao usar select *, essa revisão geralmente é ignorada porque o desenvolvedor assume que nada irá quebrar. E, de fato, nada pode parecer explicitamente quebrar, mas as consultas agora podem começar a retornar a coisa errada. Só porque nada interrompe explicitamente, não significa que não devam ter havido alterações nas consultas.

HLGEM
fonte
0

porque "select *" desperdiçará memória quando você não precisar de todos os campos. Mas, para o servidor sql, o desempenho deles é o mesmo.

FloatFish
fonte