¿Por qué SELECT * se considera perjudicial?

256

¿Por qué es una SELECT *mala práctica? ¿No significaría menos código para cambiar si agrega una nueva columna que desea?

Entiendo que SELECT COUNT(*)es un problema de rendimiento en algunos DB, pero ¿y si realmente quisieras cada columna?

Theodore R. Smith
fuente
30
SELECT COUNT(*)ser malo es increíblemente viejo y anticuado . Para obtener información sobre SELECT *: consulte: stackoverflow.com/questions/1960036/…
OMG Ponies
8
SELECT COUNT(*)da una respuesta diferente a SELECT COUNT(SomeColumn)menos que la columna sea una columna NO NULA. Y el optimizador puede dar SELECT COUNT(*)un tratamiento especial, y generalmente lo hace. También tenga en cuenta que WHERE EXISTS(SELECT * FROM SomeTable WHERE ...)se le da un tratamiento de caso especial.
Jonathan Leffler
3
@Michael Mrozek, en realidad es lo contrario de la pregunta. Pregunto si alguna vez fue dañino, no si alguna vez no fue dañino.
Theodore R. Smith
1
@Bytecode Ninja: específicamente, MySQL con el motor MyISAM tiene una optimización para COUNT (*): mysqlperformanceblog.com/2007/04/10/count-vs-countcol
Piskvor dejó el edificio el
1
Para SQL Server, consulte sqlblog.com/blogs/aaron_bertrand/archive/2009/10/10/…
Aaron Bertrand

Respuestas:

312

Realmente hay tres razones principales:

  • Ineficiencia en el traslado de datos al consumidor. Cuando SELECCIONA *, a menudo recupera más columnas de la base de datos de las que su aplicación realmente necesita para funcionar. Esto hace que se muevan más datos del servidor de la base de datos al cliente, lo que ralentiza el acceso y aumenta la carga en sus máquinas, además de tomar más tiempo para viajar a través de la red. Esto es especialmente cierto cuando alguien agrega nuevas columnas a las tablas subyacentes que no existían y que no eran necesarias cuando los consumidores originales codificaban su acceso a datos.

  • Problemas de indexación. Considere un escenario en el que desea ajustar una consulta a un alto nivel de rendimiento. Si tuviera que usar *, y devolviera más columnas de las que realmente necesitaba, el servidor a menudo tendría que realizar métodos más caros para recuperar sus datos de lo que de otro modo podría. Por ejemplo, no podría crear un índice que simplemente cubriera las columnas en su lista SELECT, e incluso si lo hiciera (incluidas todas las columnas [ estremecimiento ]), el siguiente tipo que apareció y agregó una columna al subyacente la tabla haría que el optimizador ignore su índice de cobertura optimizado, y es probable que encuentre que el rendimiento de su consulta se reduciría sustancialmente sin razón aparente.

  • Problemas vinculantes. Cuando SELECCIONA *, es posible recuperar dos columnas del mismo nombre de dos tablas diferentes. Esto a menudo puede bloquear su consumidor de datos. Imagine una consulta que une dos tablas, las cuales contienen una columna llamada "ID". ¿Cómo sabría un consumidor cuál era cuál? SELECT * también puede confundir las vistas (al menos en algunas versiones de SQL Server) cuando cambian las estructuras de la tabla subyacente: la vista no se reconstruye y los datos que regresan pueden no tener sentido . Y lo peor de todo es que puedes tener cuidado de nombrar tus columnas como quieras, pero el próximo tipo que aparezca puede no tener forma de saber que tiene que preocuparse por agregar una columna que colisionará con tu ya desarrollado nombres

Pero no todo es malo para SELECT *. Lo uso generosamente para estos casos de uso:

  • Consultas ad-hoc. Cuando intento depurar algo, especialmente en una mesa estrecha con la que no estoy familiarizado, SELECT * es a menudo mi mejor amigo. Me ayuda a ver lo que está sucediendo sin tener que investigar mucho sobre cuáles son los nombres de las columnas subyacentes. Esto se convierte en un "plus" más grande cuanto más se alargan los nombres de las columnas.

  • Cuando * significa "una fila". En los siguientes casos de uso, SELECT * está bien, y los rumores de que es un asesino de rendimiento son solo leyendas urbanas que pueden haber tenido cierta validez hace muchos años, pero ahora no:

    SELECT COUNT(*) FROM table;

    en este caso, * significa "contar las filas". Si usara un nombre de columna en lugar de *, contaría las filas donde el valor de esa columna no era nulo . COUNT (*), para mí, realmente lleva a casa el concepto de que estás contando filas , y evitas casos extraños causados ​​por la eliminación de NULL de tus agregados.

    Lo mismo ocurre con este tipo de consulta:

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

    en cualquier base de datos que valga la pena, * solo significa "una fila". No importa lo que pones en la subconsulta. Algunas personas usan la identificación de b en la lista SELECCIONAR, o usarán el número 1, pero IMO esas convenciones son bastante absurdas. Lo que quieres decir es "contar la fila", y eso es lo que * significa. La mayoría de los optimizadores de consultas son lo suficientemente inteligentes como para saber esto. (Aunque, para ser sincero, solo sé que esto es cierto con SQL Server y Oracle).

Dave Markle
fuente
17
Usar "SELECT id, name" es tan probable como "SELECT *" para seleccionar dos columnas del mismo nombre de dos tablas diferentes cuando se usan combinaciones. El prefijo con el nombre de la tabla resuelve el problema en ambos casos.
Michał Tatarynowicz
1
Sé que esto es más antiguo, pero es lo que se extrajo mientras buscaba en Google, así que pregunto. "Cuando * significa" una fila ". En los siguientes casos de uso, SELECT * está bien, y los rumores de que es un asesino de rendimiento son solo leyendas urbanas ..." ¿Tienes alguna referencia aquí? ¿Es esta declaración debido a que el hardware es más potente (si ese es el caso, no significa que no sea ineficiente, solo que es menos probable que lo note). No estoy tratando de adivinar per se, solo me pregunto de dónde viene esta declaración.
Jared
66
En cuanto a las referencias, puede examinar los planes de consulta: son idénticos en los casos en que tiene un "*" en la subconsulta en comparación con cuando selecciona una columna. Son idénticos porque el optimizador basado en costos "reconoce" que semánticamente, se trata de cualquier fila que satisfaga los criterios, no se trata de hardware o velocidad.
Dave Markle
44
Una ventaja más del uso *es que, en algunas situaciones, puede aprovechar mejor los sistemas de caché de MySQL. Si se está utilizando un gran número de similares selectconsultas que soliciten los diferentes nombres de columna ( select A where X, select B where X, ...) utilizando una select * where Xpermitirán la caché para manejar un mayor número de las preguntas que pueden resultar en un aumento sustancial del rendimiento. Es un escenario específico de la aplicación, pero vale la pena tenerlo en cuenta.
Ben D
2
Más de 8 años después, pero quiero agregar un punto sobre la ambigüedad que no se mencionó. Trabajar con más de 200 tablas en una base de datos y tener una combinación de convenciones de nomenclatura. Al revisar el código que interactúa con los resultados de la consulta, SELECT *obliga a los desarrolladores a mirar los esquemas de la tabla involucrados, para determinar las columnas afectadas / disponibles, como dentro de un foreacho serialize. La tarea de mirar repetidamente los esquemas para rastrear lo que está sucediendo, inevitablemente aumentará el tiempo total involucrado tanto en la depuración como en el desarrollo de código relacionado.
fyrye
91

El carácter de asterisco, "*", en la instrucción SELECT es una forma abreviada de todas las columnas en las tablas involucradas en la consulta.

Actuación

La *taquigrafía puede ser más lenta porque:

  • No todos los campos están indexados, lo que obliga a un escaneo completo de la tabla, menos eficiente
  • Lo que guarde para enviar SELECT *por cable corre el riesgo de escanear una tabla completa
  • Devolver más datos de los necesarios
  • La devolución de columnas finales con el tipo de datos de longitud variable puede generar una sobrecarga de búsqueda

Mantenimiento

Al usar SELECT *:

  • Alguien que no esté familiarizado con la base de código se vería obligado a consultar la documentación para saber qué columnas se devuelven antes de poder realizar cambios competentes. Hacer que el código sea más legible, minimizando la ambigüedad y el trabajo necesarios para las personas que no están familiarizadas con el código ahorra más tiempo y esfuerzo a largo plazo.
  • Si el código depende del orden de las columnas, SELECT *ocultará un error a la espera de que ocurra si una tabla ha cambiado su orden de columnas.
  • Incluso si necesita cada columna en el momento en que se escribe la consulta, ese podría no ser el caso en el futuro
  • el uso complica el perfilado

Diseño

SELECT *es un antipatrón :

  • El propósito de la consulta es menos obvio; las columnas utilizadas por la aplicación son opacas
  • Rompe la regla de modularidad sobre el uso de mecanografía estricta siempre que sea posible. Explícito es casi universalmente mejor.

¿Cuándo se debe usar "SELECCIONAR *"?

Es aceptable usarlo SELECT *cuando existe la necesidad explícita de cada columna en la (s) tabla (s) involucradas, en oposición a cada columna que existía cuando se escribió la consulta. La base de datos expandirá internamente el * en la lista completa de columnas; no hay diferencia de rendimiento.

De lo contrario, enumere explícitamente cada columna que se utilizará en la consulta, preferiblemente mientras usa un alias de tabla.

Ponis OMG
fuente
20

Incluso si quisiera seleccionar cada columna ahora, es posible que no desee seleccionar cada columna después de que alguien agregue una o más columnas nuevas. Si escribe la consulta, SELECT *corre el riesgo de que en algún momento alguien agregue una columna de texto, lo que hace que su consulta se ejecute más lentamente, aunque en realidad no necesita esa columna.

¿No significaría menos código para cambiar si agrega una nueva columna que desea?

Lo más probable es que si realmente desea utilizar la nueva columna, de todos modos tendrá que hacer muchos otros cambios en su código. Solo está guardando , new_column, solo unos pocos caracteres de escritura.

Mark Byers
fuente
21
Especialmente si esa nueva columna es un BLOB de tres megabytes
Matti Virkkunen
2
@Matti - ¡Pero espero que piensen más que "Hey, vamos a colocar una enorme columna BLOB en esta mesa!" . (Sí, los tontos esperan saberlo, pero ¿no puede un chico soñar?)
ChaosPandion
55
El rendimiento es un aspecto, pero a menudo también hay un aspecto de corrección: la forma del resultado proyectado *puede cambiar inesperadamente y esto puede causar estragos en la aplicación misma: las columnas a las que hace referencia ordinal (por ejemplo, sqldatareader.getstring (2)) se recuperan repentinamente una columna diferente , cualquiera INSERT ... SELECT *se romperá y así sucesivamente.
Remus Rusanu
2
@chaos: poner blobs en las mesas realmente no va a afectar mucho tu rendimiento ... A menos que uses SELECT * ... ;-)
Dave Markle
2
No debe preocuparse por el rendimiento hasta que cause problemas reales. Y además, SELECT *no se trata de salvar pocos personajes. Se trata de ahorrar horas de tiempo de depuración porque es fácil olvidar especificar nuevas columnas agregadas.
Lewis
4

Si nombra las columnas en una instrucción SELECT, se devolverán en el orden especificado y, por lo tanto, se puede hacer referencia a ellas de forma segura mediante un índice numérico. Si usa "SELECCIONAR *", puede terminar recibiendo las columnas en secuencia arbitraria y, por lo tanto, solo puede usar las columnas de forma segura por su nombre. A menos que sepa de antemano lo que querrá hacer con cualquier columna nueva que se agregue a la base de datos, la acción correcta más probable es ignorarla. Si va a ignorar las nuevas columnas que se agregan a la base de datos, no hay ningún beneficio en recuperarlas.

Super gato
fuente
"por lo tanto puede ser referenciado por segura índice numérico", pero que sería lo suficientemente estúpido para siempre tratar de hacer referencia a una columna de índice numérico en lugar de de su nombre !? Es un antipatrón mucho peor que usar select * en una vista.
MGOwen
@ MGOwen: Usar select *y luego usar las columnas por índice sería horrible, pero usar select X, Y, Zo select A,B,Cy luego pasar el lector de datos resultante al código que espera hacer algo con los datos en las columnas 0, 1 y 2 parecería una forma perfectamente razonable de permita que el mismo código actúe sobre X, Y, Z o A, B, C. Tenga en cuenta que los índices de las columnas dependerán de su ubicación dentro de la instrucción SELECT, en lugar de su orden en la base de datos.
Supercat
3

En muchas situaciones, SELECT * causará errores en tiempo de ejecución en su aplicación, en lugar de en tiempo de diseño. Oculta el conocimiento de cambios de columna o malas referencias en sus aplicaciones.

Andrew Lewis
fuente
1
Entonces, ¿cómo ayuda nombrar las columnas? En SQL Server, las consultas existentes, incrustadas en código o SP, no se quejarán hasta que se ejecuten, incluso si ha nombrado las columnas. Los nuevos fallarán cuando los pruebe, pero tiene mucho tiempo para buscar SP afectados por los cambios en la tabla. ¿A qué tipo de situaciones te refieres que quedarían atrapadas en el momento del diseño?
ChrisA
3

Si realmente quiere cada columna, no he visto una diferencia de rendimiento entre select (*) y nombrar las columnas. El controlador para nombrar las columnas podría ser simplemente ser explícito sobre qué columnas espera ver en su código.

Sin embargo, a menudo, no desea todas las columnas y la selección (*) puede resultar en un trabajo innecesario para el servidor de la base de datos e información innecesaria que debe pasarse a través de la red. Es poco probable que cause un problema notable a menos que el sistema sea muy utilizado o la conectividad de la red sea lenta.

brabster
fuente
3

Piense en ello como reducir el acoplamiento entre la aplicación y la base de datos.

Para resumir el aspecto del "olor del código":
SELECT *crea una dependencia dinámica entre la aplicación y el esquema. Restringir su uso es una forma de hacer que la dependencia esté más definida, de lo contrario, un cambio en la base de datos tiene una mayor probabilidad de bloquear su aplicación.

Kelly S. French
fuente
3

Si agrega campos a la tabla, se incluirán automáticamente en todas las consultas que utilice select *. Esto puede parecer conveniente, pero hará que su aplicación sea más lenta ya que obtiene más datos de los que necesita, y en realidad la bloqueará en algún momento.

Existe un límite para la cantidad de datos que puede obtener en cada fila de un resultado. Si agrega campos a sus tablas para que un resultado termine por encima de ese límite, recibirá un mensaje de error cuando intente ejecutar la consulta.

Este es el tipo de errores que son difíciles de encontrar. Realiza un cambio en un lugar y explota en otro lugar que en realidad no utiliza los nuevos datos. Incluso puede ser una consulta de uso menos frecuente, por lo que lleva un tiempo antes de que alguien la use, lo que dificulta aún más la conexión del error con el cambio.

Si especifica qué campos desea en el resultado, está a salvo de este tipo de desbordamiento de gastos generales.

Guffa
fuente
2

Referencia tomada de este artículo.

Nunca vaya con "SELECCIONAR *",

Solo he encontrado una razón para usar "SELECT *"

Si tiene requisitos especiales y creó un entorno dinámico cuando la columna de agregar o eliminar se maneja automáticamente por código de aplicación. En este caso especial, no necesita cambiar el código de la aplicación y la base de datos y esto afectará automáticamente el entorno de producción. En este caso puede usar "SELECCIONAR *".

Anvesh
fuente
1

En general, debe ajustar los resultados de su SELECT * ...en estructuras de datos de varios tipos. Sin especificar en qué orden llegan los resultados, puede ser complicado alinear todo correctamente (y los campos más oscuros son mucho más fáciles de omitir).

De esta manera, puede agregar campos a sus tablas (incluso en el medio de ellas) por varias razones sin romper el código de acceso sql en toda la aplicación.

jkerian
fuente
1

El uso SELECT *cuando solo necesita un par de columnas significa que se transfieren muchos más datos de los que necesita. Esto agrega procesamiento en la base de datos y aumenta la latencia para llevar los datos al cliente. Agregue a esto que usará más memoria cuando se cargue, en algunos casos significativamente más, como archivos BLOB grandes, se trata principalmente de eficiencia.

Además de esto, sin embargo, es más fácil ver al mirar la consulta qué columnas se están cargando, sin tener que buscar lo que hay en la tabla.

Sí, si agrega una columna adicional, sería más rápido, pero en la mayoría de los casos, querría / necesitaría cambiar su código usando la consulta para aceptar las nuevas columnas de todos modos, y existe la posibilidad de que obtenga las que no tiene ' No querer / esperar puede causar problemas. Por ejemplo, si toma todas las columnas, confíe en el orden en un bucle para asignar variables, luego agregue una, o si cambian los pedidos de la columna (visto que sucede al restaurar desde una copia de seguridad) puede tirar todo.

Este es también el mismo tipo de razonamiento por el que si está haciendo un INSERTsiempre debe especificar las columnas.

Tarka
fuente
1

No creo que realmente pueda haber una regla general para esto. En muchos casos, he evitado SELECT *, pero también he trabajado con marcos de datos donde SELECT * fue muy beneficioso.

Como con todas las cosas, hay beneficios y costos. Creo que parte de la ecuación beneficio vs. costo es cuánto control tiene sobre las estructuras de datos. En los casos en que SELECT * funcionaba bien, las estructuras de datos estaban estrictamente controladas (era un software minorista), por lo que no había mucho riesgo de que alguien se escabullera de un enorme campo BLOB en una tabla.

JMarsch
fuente
1

Seleccionar con el nombre de la columna aumenta la probabilidad de que el motor de la base de datos pueda acceder a los datos de los índices en lugar de consultar los datos de la tabla.

SELECT * expone su sistema a cambios inesperados en el rendimiento y la funcionalidad en el caso de que cambie el esquema de su base de datos porque va a agregar columnas nuevas a la tabla, aunque su código no esté preparado para usar o presentar esos nuevos datos.

Aradhana Mohanty
fuente
1

También hay una razón más pragmática: el dinero. Cuando utiliza la base de datos en la nube y tiene que pagar por los datos procesados, no hay explicación para leer los datos que descartará de inmediato.

Por ejemplo: BigQuery :

Consulta de precios

El precio de consulta se refiere al costo de ejecutar sus comandos SQL y funciones definidas por el usuario. BigQuery cobra por consultas utilizando una métrica: el número de bytes procesados.

y Control de proyección - Evite SELECCIONAR * :

Práctica recomendada: Proyección de control: consulte solo las columnas que necesita.

La proyección se refiere al número de columnas que lee su consulta. La proyección de columnas en exceso incurre en E / S adicionales (desperdiciadas) y materialización (resultados de escritura).

Usar SELECT * es la forma más costosa de consultar datos. Cuando usa SELECT *, BigQuery realiza un análisis completo de cada columna de la tabla.

Lukasz Szozda
fuente
0

Comprenda sus requisitos antes de diseñar el esquema (si es posible).

Conozca los datos, 1) indexación 2) tipo de almacenamiento utilizado, 3) motor o características del proveedor; es decir ... almacenamiento en caché, capacidades en memoria 4) tipos de datos 5) tamaño de la tabla 6) frecuencia de consulta 7) cargas de trabajo relacionadas si el recurso se comparte 8) Prueba

A) Los requisitos variarán. Si el hardware no puede soportar la carga de trabajo esperada, debe reevaluar cómo proporcionar los requisitos en la carga de trabajo. En cuanto a la columna de adición a la tabla. Si la base de datos admite vistas, puede crear una vista indizada (?) De los datos específicos con las columnas con nombre específicas (en lugar de seleccionar '*'). Revise periódicamente sus datos y su esquema para asegurarse de que nunca se encuentre con el síndrome "Basura de entrada" -> "Basura de salida".

Asumiendo que no hay otra solución; Puede tener en cuenta lo siguiente. Siempre hay múltiples soluciones a un problema.

1) Indexación: el select * ejecutará un escaneo de tabla. Dependiendo de varios factores, esto puede implicar una búsqueda de disco y / o contención con otras consultas. Si la tabla es multipropósito, asegúrese de que todas las consultas sean efectivas y se ejecuten por debajo de los tiempos objetivo. Si hay una gran cantidad de datos y su red u otro recurso no está sintonizado; necesitas tener esto en cuenta. La base de datos es un entorno compartido.

2) tipo de almacenamiento. Es decir: si está utilizando SSD, disco o memoria. Los tiempos de E / S y la carga en el sistema / CPU variarán.

3) ¿Puede el DBA ajustar la base de datos / tablas para un mayor rendimiento? Suponiendo por cualquier razón, los equipos han decidido que seleccionar '*' es la mejor solución al problema; ¿Se puede cargar el DB o la tabla en la memoria? (U otro método ... ¿tal vez la respuesta fue diseñada para responder con un retraso de 2-3 segundos? --- mientras se reproduce un anuncio para obtener los ingresos de la empresa ...)

4) Comience en la línea de base. Comprenda sus tipos de datos y cómo se presentarán los resultados. Tipos de datos más pequeños, el número de campos reduce la cantidad de datos devueltos en el conjunto de resultados. Esto deja recursos disponibles para otras necesidades del sistema. Los recursos del sistema generalmente tienen un límite; 'siempre' trabaja por debajo de estos límites para garantizar la estabilidad y un comportamiento predecible.

5) tamaño de la tabla / datos. seleccione '*' es común con tablas pequeñas. Normalmente encajan en la memoria, y los tiempos de respuesta son rápidos. De nuevo ... revise sus requisitos. Plan para el arrastre de características; siempre planifique para las necesidades actuales y futuras posibles.

6) Frecuencia de consulta / consultas. Tenga en cuenta otras cargas de trabajo en el sistema. Si esta consulta se dispara cada segundo, y la tabla es pequeña. El conjunto de resultados puede diseñarse para permanecer en caché / memoria. Sin embargo, si la consulta es un proceso por lotes frecuente con Gigabytes / Terabytes de datos ... es mejor que dedique recursos adicionales para garantizar que otras cargas de trabajo no se vean afectadas.

7) Cargas de trabajo relacionadas. Comprenda cómo se utilizan los recursos. ¿La red / sistema / base de datos / tabla / aplicación está dedicada o compartida? ¿Quiénes son los interesados? ¿Es esto para producción, desarrollo o control de calidad? ¿Es esta una "solución rápida" temporal? ¿Has probado el escenario? Te sorprenderá cuántos problemas pueden existir en el hardware actual hoy. (Sí, el rendimiento es rápido ... pero el diseño / rendimiento todavía está degradado). ¿Necesita el sistema realizar 10K consultas por segundo frente a 5-10 consultas por segundo? ¿El servidor de la base de datos está dedicado, o realiza otras aplicaciones? Algunas aplicaciones / idiomas; Las O / S consumirán el 100% de la memoria causando diversos síntomas / problemas.

8) Prueba: Pon a prueba tus teorías y comprende todo lo que puedas al respecto. Su problema de selección '*' puede ser un gran problema, o puede ser algo de lo que ni siquiera necesita preocuparse.

kllee
fuente