¿Cuál es la razón para no usar select *?

136

He visto a varias personas afirmar que debe nombrar específicamente cada columna que desee en su consulta de selección.

Suponiendo que voy a usar todas las columnas de todos modos, ¿por qué no usaría SELECT *?

Incluso teniendo en cuenta la pregunta * Consulta SQL: seleccione * de la vista o Seleccione col1, col2, ... colN de la vista *, no creo que este sea un duplicado exacto ya que estoy abordando el problema desde una perspectiva ligeramente diferente.

Uno de nuestros principios es no optimizar antes de que sea el momento. Con eso en mente, parece que usar SELECT *debería ser el método preferido hasta que se demuestre que se trata de un problema de recursos o que el esquema esté prácticamente establecido. Lo cual, como sabemos, no ocurrirá hasta que el desarrollo se haya completado por completo.

Dicho esto, ¿hay un problema primordial para no usar SELECT *?

Yo no
fuente

Respuestas:

168

La esencia de la cita de no optimizar prematuramente es buscar un código simple y directo y luego usar un generador de perfiles para señalar los puntos calientes, que luego puede optimizar para ser eficiente.

Cuando usa select *, hace que sea imposible crear un perfil, por lo tanto, no está escribiendo un código claro y directo y va en contra del espíritu de la cita. select *Es un antipatrón.


Por lo tanto, seleccionar columnas no es una optimización prematura. Algunas cosas fuera de mi cabeza ...

  1. Si especifica columnas en una instrucción SQL, el motor de ejecución de SQL producirá un error si esa columna se elimina de la tabla y se ejecuta la consulta.
  2. Puede escanear más fácilmente el código donde se usa esa columna.
  3. Siempre debe escribir consultas para recuperar la menor cantidad de información.
  4. Como otros mencionan, si usa el acceso a la columna ordinal, nunca debe usar select *
  5. Si su declaración SQL une tablas, select * le da todas las columnas de todas las tablas en la combinación

El corolario es que usando select *...

  1. Las columnas utilizadas por la aplicación son opacas.
  2. Los DBA y sus analizadores de consultas no pueden ayudar al bajo rendimiento de su aplicación
  3. El código es más frágil cuando se producen cambios.
  4. Su base de datos y red están sufriendo porque están trayendo demasiados datos (E / S)
  5. Las optimizaciones del motor de base de datos son mínimas ya que recupera todos los datos independientemente (lógico).

Escribir un SQL correcto es tan fácil como escribir Select *. Entonces, la persona perezosa real escribe el SQL apropiado porque no quiere volver a visitar el código e intenta recordar lo que estaba haciendo cuando lo hizo. No quieren explicar a los DBA sobre cada fragmento de código. No quieren explicar a sus clientes por qué la aplicación se ejecuta como un perro.

Robert Paulson
fuente
2
En su primera sección, el punto # 5 debería leer "select * le da todas las columnas de todas las tablas en la unión". En su segunda sección, los puntos 2 y 5 no son necesariamente verdaderos, y no deben enumerarse como razones para no usar "select *".
jimmyorr
1
@uglysmurf, gracias por la corrección, pero en lo que respecta a 2 y 5, aunque pueden no ser necesariamente válidos para todas las bases de datos / dba en todos los casos, creo que son importantes y válidos para la mayoría de los casos y los dejarán. Usar 'select *' nunca facilitó el trabajo de un dba.
Robert Paulson
11
Argumentaría que el n. ° 3 (código frágil) no es realmente cierto. Dependiendo de la implementación, Select * podría hacerlo MENOS quebradizo, pero no veo cómo podría ser más.
JohnFx
2
@ JohnFx, supongo que define frágil de manera diferente. Frágil se define normalmente como "se rompe fácilmente". Tener dependencias desconocidas o difíciles de encontrar porque cada pieza de código usará columnas diferentes significa que no puedo cambiar fácilmente nada a nivel de datos sin una regresión completa ... lo cual parece frágil.
Robert Paulson
9
@mavnn, wrt fragilidad, me temo que esto se está convirtiendo en un problema de semántica en mi elección de la palabra frágil. Mi última palabra es decir que de todos modos hace poca diferencia. El único escenario es renombrar / eliminar columnas. Simplemente está moviendo el descanso desde cuando se ejecuta el sql (explícito) frente al descanso cuando se consumen los resultados. La forma en que se consume el resultado de la consulta puede variar, y el código puede fallar silenciosamente o no, pero el motor de ejecución SQL fallará definitivamente con SQL inválido. Entonces, ¿select * te ayudó? La falla explícita de la OMI más cercana a la base de datos para un problema de base de datos es mejor. Thx
Robert Paulson
42

Si su código depende de que las columnas estén en un orden específico, su código se romperá cuando haya cambios en la tabla. Además, puede estar obteniendo demasiado de la tabla cuando selecciona *, especialmente si hay un campo binario en la tabla.

El hecho de que esté utilizando todas las columnas ahora, no significa que alguien más no va a agregar una columna adicional a la tabla.

También agrega sobrecarga al almacenamiento en caché de ejecución del plan, ya que tiene que recuperar los metadatos sobre la tabla para saber qué columnas están en *.

Beto
fuente
44
Buena respuesta, pero cambiaría el "código se romperá" a "el código PUEDE romperse". Ese es el verdadero problema aquí, el uso "select *" NO SIEMPRE produce un cambio importante. Y cuando ocurre la ruptura, generalmente está muy desacoplado del uso que termina roto.
BQ.
44
Si alguien hace referencia a columnas ordinariamente en su código, está en problemas independientemente de si usa SELECT * o no. La sobrecarga de ejecución del plan es trivial, y de todos modos no importaría una vez que el plan se almacena en caché.
MusiGenesis
1
Entonces, el error del programador radica en escribir código que depende de la secuencia de las columnas. Nunca necesitas hacer eso.
dkretz
1
@doofledorfer - nunca digas nunca Es más rápido acceder a columnas ordinales, y es práctico a veces. Es un error mayor usar select * que usar acceso ordinal.
Robert Paulson el
23

Una razón importante es que si alguna vez agrega / elimina columnas de su tabla, cualquier consulta / procedimiento que realice una llamada SELECT * ahora obtendrá más o menos columnas de datos de lo esperado.

ahockley
fuente
3
De todos modos, nunca debe escribir código que dependa del número de columnas devueltas.
dkretz
44
Pero todos escriben código que requiere que los programadores sepan qué datos están regresando. No puede Ctrl + F el nombre de su columna si está oculto en un SELECT *.
Lotus Notes
17
  1. De manera indirecta, está rompiendo la regla de modularidad sobre el uso de mecanografía estricta siempre que sea posible. Explícito es casi universalmente mejor.

  2. Incluso si ahora necesita cada columna de la tabla, se podrían agregar más más adelante, que se desplegarán cada vez que ejecute la consulta y podrían afectar el rendimiento. Daña el rendimiento porque

    • Estás tirando más datos por el cable; y
    • Porque puede vencer la capacidad del optimizador de extraer los datos directamente del índice (para consultas en columnas que forman parte de un índice) en lugar de buscar en la tabla misma

Cuando TO usar seleccione *

Cuando NECESITA explícitamente cada columna de la tabla, en lugar de necesitar cada columna de la tabla QUE EXISTÍA EN EL MOMENTO EN QUE ESCRIBIÓ LA CONSULTA. Por ejemplo, si estuviera escribiendo una aplicación de administración de DB que necesitara mostrar todo el contenido de la tabla (lo que sea que sea) podría usar ese enfoque.

JohnFx
fuente
1
Otro momento para usar SELECT *sería cuando esté haciendo consultas de prueba con el cliente db.
cdmckay
Parece una extraña excepción dado el contexto de la pregunta. Además de guardar algo de escritura, ¿cuál es la ventaja de hacer esto para consultas de prueba?
JohnFx
También SELECT * FROM (SELECT a, b, c FROM table) está bien.
kmkaplan
12

Hay unas pocas razones:

  1. Si el número de columnas en una base de datos cambia y su aplicación espera que haya un cierto número ...
  2. Si el orden de las columnas en una base de datos cambia y su aplicación espera que estén en un cierto orden ...
  3. Memoria sobrecarga. 8 columnas INTEGER innecesarias agregarían 32 bytes de memoria desperdiciada. Eso no parece mucho, pero esto es para cada consulta e INTEGER es uno de los tipos de columnas pequeñas ... es más probable que las columnas adicionales sean columnas VARCHAR o TEXT, que se suman más rápido.
  4. Gastos generales de red. Relacionado con la sobrecarga de memoria: si publico 30,000 consultas y tengo 8 columnas INTEGER innecesarias, he perdido 960kB de ancho de banda. Es probable que las columnas VARCHAR y TEXT sean considerablemente más grandes.

Nota: Elegí INTEGER en el ejemplo anterior porque tienen un tamaño fijo de 4 bytes.

Powerlord
fuente
1 y 2 serían un olor a código y 3 y 4 sonarían a optimización prematura
NikkyD
7

Si su aplicación obtiene datos con SELECT * y se cambia la estructura de la tabla en la base de datos (digamos que se elimina una columna), su aplicación fallará en cada lugar que haga referencia al campo faltante. Si en su lugar incluye todas las columnas en su consulta, su aplicación se dividirá en el (con suerte) un lugar donde inicialmente obtiene los datos, facilitando la solución.

Dicho esto, hay una serie de situaciones en las que SELECT * es deseable. Una es una situación que encuentro todo el tiempo, donde necesito replicar una tabla completa en otra base de datos (como SQL Server a DB2, por ejemplo). Otra es una aplicación escrita para mostrar tablas genéricamente (es decir, sin ningún conocimiento de ninguna tabla en particular).

MusiGenesis
fuente
La pregunta no es 'es select * siempre deseable', por lo que la segunda parte de su respuesta es irrelevante. La pregunta establece que usar 'select *' debería ser preferible, lo que por supuesto es completo.
Robert Paulson
Sí, mi segunda parte es irrelevante. OQ cambió la pregunta al estado SELECCIONAR * es preferible, y sí, eso es un poco tonto.
MusiGenesis
Ah, sí, lo siento, la pregunta cambió su dirección después de su respuesta.
Robert Paulson
Eso está bien. Incluso Mozart fue editor ( stackoverflow.com/questions/292682/… ). Mi publicación original sugirió que el uso de SELECT * condujo al canibalismo. :)
MusiGenesis
3

De hecho, noté un comportamiento extraño cuando lo usé select *en vistas en SQL Server 2005.

Ejecute la siguiente consulta y verá lo que quiero decir.

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 los resultados de las últimas 2 declaraciones de selección. Creo que lo que verá es el resultado de Seleccionar * haciendo referencia a columnas por índice en lugar de nombre.

Si reconstruye la vista, volverá a funcionar bien.

EDITAR

He agregado una pregunta separada, * "seleccionar * de la tabla" frente a "seleccionar colA, colB, etc. de la tabla" comportamiento interesante en SQL Server 2005 * para analizar ese comportamiento con más detalles.

kristof
fuente
2

Puede unir dos tablas y usar la columna A de la segunda tabla. Si luego agrega la columna A a la primera tabla (con el mismo nombre pero posiblemente con un significado diferente), lo más probable es que obtenga los valores de la primera tabla y no de la segunda como antes. Eso no sucederá si especifica explícitamente las columnas que desea seleccionar.

Por supuesto, especificar las columnas también a veces causa errores si olvida agregar las nuevas columnas a cada cláusula select. Si no se necesita la nueva columna cada vez que se ejecuta la consulta, puede pasar algún tiempo antes de que se note el error.

Kaniu
fuente
2

Entiendo a dónde vas con respecto a la optimización prematura, pero eso realmente solo llega a un punto. La intención es evitar una optimización innecesaria al principio. ¿Sus tablas no están indexadas? ¿Usaría nvarchar (4000) para almacenar un código postal?

Como otros han señalado, hay otros aspectos positivos para especificar cada columna que pretende utilizar en la consulta (como la capacidad de mantenimiento).

Jim BG
fuente
2

Cuando estás especificando columnas, también te estás atando a un conjunto específico de columnas y haciéndote menos flexible, haciendo que Feuerstein ruede, bueno, donde sea que esté. Solo un pensamiento.

orbfish
fuente
1
No tengo ni idea de quién es Feuerstein. Intenté buscar en Google y encontré un psicólogo, un personaje de televisión y un blogger, así que lo mejor que pude encontrar fue una broma.
NotMe
Autor de los libros de O'Reilly sobre PL / SQL. Intenta buscar en Google "feuerstein sql" en lugar de solo "feuerstein".
pez orb
2

SELECCIONAR * no siempre es malo. En mi opinión al menos. Lo uso con bastante frecuencia para consultas dinámicas que devuelven una tabla completa, más algunos campos calculados.

Por ejemplo, quiero calcular geometrías geográficas a partir de una tabla "normal", que es una tabla sin ningún campo de geometría, pero con campos que contienen coordenadas. Yo uso postgresql, y su extensión espacial postgis. Pero el principio se aplica a muchos otros casos.

Un ejemplo:

  • una tabla de lugares, con coordenadas almacenadas en campos etiquetados x, y, z:

    CREATE TABLE lugares (place_id entero, x numeric (10, 3), y numeric (10, 3), z numeric (10, 3), descripción varchar);

  • vamos a alimentarlo con algunos valores de ejemplo:

    INSERTAR EN lugares (place_id, x, y, z, descripción) 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,' Condón, Cathédrale St-Pierre ');

  • Quiero poder asignar el contenido de esta tabla, utilizando algún cliente SIG. La forma normal es agregar un campo de geometría a la tabla y construir la geometría, en función de las coordenadas. Pero preferiría obtener una consulta dinámica: de esta manera, cuando cambio las coordenadas (correcciones, más precisión, etc.), los objetos mapeados realmente se mueven, dinámicamente. Así que aquí está la consulta con SELECT * :

    CREAR O REEMPLAZAR VISUALIZAR lugares_puntos COMO
    SELECCIONAR *,
    GeomFromewkt ('SRID = 4326; POINT (' || x || '' || y || '' || z || ')')
    DESDE lugares;

    Consulte postgis, para el uso de la función GeomFromewkt ().

  • Aquí está el resultado:

    SELECCIONAR * DESDE lugares_puntos;

place_id | x | y | z | descripción | geomfromewkt                            
---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2,295 | 48,863 | 64.000 | París, Place de l'Étoile | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2,945 | 48,858 | 40.000 | París, Tour Eiffel | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0,373 | 43,958 | 90.000 | Condón, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 líneas)

La columna de la derecha ahora puede ser utilizada por cualquier programa SIG para mapear adecuadamente los puntos.

  • Si, en el futuro, se agregan algunos campos a la tabla: no se preocupe, solo tengo que volver a ejecutar la misma definición VIEW.

Desearía que la definición de la VISTA pudiera mantenerse "tal cual", con el *, pero hélas no es el caso: así es como postgresql la almacena internamente:

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

Pierre
fuente
1

Incluso si usa todas las columnas pero aborda la matriz de filas por índice numérico, tendrá problemas si agrega otra fila más adelante.

¡Entonces, básicamente, es una cuestión de mantenibilidad! Si no utiliza el selector *, no tendrá que preocuparse por sus consultas.

Markus
fuente
1

Si selecciona solo las columnas que necesita, el conjunto de datos en la memoria será más pequeño y, por lo tanto, su aplicación se acelerará.

Además, muchas herramientas (por ejemplo, procedimientos almacenados) también almacenan planes de ejecución de consultas en caché. Si luego agrega o elimina una columna (particularmente fácil si está seleccionando una vista), la herramienta a menudo fallará cuando no recupere los resultados que espera.

Soldarnal
fuente
1

Hace que su código sea más ambiguo y más difícil de mantener; porque está agregando datos adicionales no utilizados al dominio, y no está claro cuál fue su intención y cuál no. (También sugiere que es posible que no lo sepa o que no le importe).

dkretz
fuente
1

Para responder su pregunta directamente: No utilice "SELECCIONAR *" cuando haga que su código sea más flexible para los cambios en las tablas subyacentes. Su código debe romperse solo cuando se realiza un cambio en la tabla que afecta directamente los requisitos de su programa.

Su aplicación debe aprovechar la capa de abstracción que proporciona el acceso relacional.

Metro
fuente
1

No uso SELECT * simplemente porque es agradable ver y saber qué campos estoy recuperando.

lkessler
fuente
1

En general, es malo usar 'select *' dentro de las vistas porque se verá obligado a recompilar la vista en caso de un cambio en la columna de la tabla. Al cambiar las columnas de la tabla subyacente de una vista, obtendrá un error para las columnas no existentes hasta que regrese y vuelva a compilar.

Christopher Klein
fuente
1

Está bien cuando lo estás haciendo, exists(select * ...)ya que nunca se expande. De lo contrario, solo es útil al explorar tablas con declaraciones de selección temporales o si tenía un CTE definido anteriormente y desea cada columna sin volver a escribirlas todas.

dotjoe
fuente
1

Solo para agregar una cosa que nadie más ha mencionado. Select *devuelve todas las columnas, alguien puede agregar una columna más tarde que no necesariamente desea que los usuarios puedan ver, como quién actualizó por última vez los datos o una marca de tiempo o notas que solo los gerentes deberían ver no todos los usuarios, etc.

Además, al agregar una columna, el impacto en el código existente debe revisarse y considerarse para ver si se necesitan cambios en función de la información almacenada en la columna. Al usar select *, esa revisión a menudo se omitirá porque el desarrollador asumirá que nada se romperá. Y, de hecho, nada puede parecer explícitamente roto, pero las consultas ahora pueden comenzar a devolver algo incorrecto. El hecho de que nada se rompa explícitamente no significa que no debería haber habido cambios en las consultas.

HLGEM
fuente
0

porque "select *" desperdiciará memoria cuando no necesite todos los campos. Pero para el servidor sql, su rendimiento es el mismo.

FloatFish
fuente