¿Select * sigue siendo un gran no-no en SQL Server 2012?


41

En los días de antaño, se consideraba un gran no-no para hacer select * from tableo select count(*) from tabledebido al éxito en el rendimiento.

¿Sigue siendo así en versiones posteriores de SQL Server (estoy usando 2012, pero supongo que la pregunta se aplicaría a 2008 - 2014)?

Editar: Dado que la gente parece estar mezclándome un poco aquí, estoy mirando esto desde un punto de vista académico / punto de vista, no si es lo "correcto" que hacer (que por supuesto no lo es)

Respuestas:


50

Si usted SELECT COUNT(*) FROM TABLEque solo devuelve una fila (el conteo), es relativamente ligero y es la forma de obtener ese dato.

Y SELECT *no es un no físico, ya que es legal y está permitido.

Sin embargo, el problema SELECT *es que puede causar mucho más movimiento de datos. Usted opera en cada columna de la tabla. Si SELECTsolo incluye unas pocas columnas, es posible que pueda obtener su respuesta de un índice o índices, lo que reduce la E / S y también el impacto en la memoria caché del servidor.

Entonces, , se recomienda como práctica general porque es un desperdicio de sus recursos.

El único beneficio real de SELECT *no es escribir todos los nombres de columna. Pero desde SSMS puede usar arrastrar y soltar para obtener los nombres de columna en su consulta y eliminar aquellos que no necesita.

Una analogía: si alguien usa SELECT *cuando no necesita cada columna, ¿lo usaría tambiénSELECT sin una WHERE(u otra cláusula limitante) cuando no necesita cada fila?


24

Además de la respuesta ya proveedor, creo que vale la pena señalar que los desarrolladores a menudo son demasiado vagos cuando trabajan con ORM modernos como Entity Framework. Mientras que los DBA hacen todo lo posible por evitarlos SELECT *, los desarrolladores a menudo escriben el equivalente semántico, por ejemplo, en c # Linq:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

En esencia, esto resultaría en lo siguiente:

SELECT * FROM MyTable WHERE FirstName = 'User'

También hay una sobrecarga adicional que aún no se ha cubierto. Esos son los recursos necesarios para procesar cada columna en cada fila hasta el objeto relevante. Además, por cada objeto guardado en la memoria, ese objeto debe limpiarse. Si solo seleccionó las columnas que necesitaba, podría guardar fácilmente más de 100mb de ram. Si bien no es una cantidad masiva por sí sola, es el efecto acumulativo de la recolección de basura, etc.

Entonces, sí, al menos para mí, es y siempre será un gran no. También tenemos que educar sobre los costos "ocultos" de hacer esto más también.

Apéndice

Aquí hay una muestra de cómo extraer solo los datos que necesita según lo solicitado en los comentarios:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });

13

Rendimiento: una consulta con SELECT * probablemente nunca será una consulta de cobertura ( explicación de conversación simple , explicación de desbordamiento de pila ).

A prueba de futuro: su consulta podría devolver las siete columnas hoy, pero si alguien agrega cinco columnas durante el próximo año, en un año su consulta devolverá doce columnas, desperdiciando E / S y CPU.

Indexación: si desea que sus vistas y funciones con valores de tabla participen en la indexación en SQL Server, esas vistas y funciones deben crearse con un esquema, que prohíbe el uso de SELECT *.

Mejor práctica : nunca usar SELECT *en código de producción.

Para las subconsultas, prefiero WHERE EXISTS ( SELECT 1 FROM … ).

Editar : Para abordar el comentario de Craig Young a continuación, el uso de "SELECCIONAR 1" en una subconsulta no es una "optimización", es para que pueda pararme frente a mi clase y decir "¡no use SELECCIONAR *, sin excepciones! "

La única excepción que se me ocurre es cuando el cliente está haciendo algún tipo de operación de tabla dinámica y requiere todas las columnas presentes y futuras.

Podría aceptar una excepción que involucre CTE y tablas derivadas, aunque me gustaría ver planes de ejecución.

Tenga en cuenta que considero COUNT(*)una excepción a esto porque es un uso sintáctico diferente de "*".


10

En SQL Server 2012 (o cualquier versión desde 2005), el uso SELECT *...es solo un posible problema de rendimiento en la instrucción SELECT de nivel superior de una consulta.

Por lo tanto, NO es un problema en Vistas (*), en subconsultas, en cláusulas EXIST, en CTE, ni en SELECT COUNT(*)..etc., etc. Tenga en cuenta que esto probablemente también sea cierto para Oracle y DB2, y tal vez PostGres (no estoy seguro) , pero es muy probable que todavía sea un problema en muchos casos para MySql.

Para entender por qué (y por qué todavía puede ser un problema en un SELECT de nivel superior), es útil comprender por qué alguna vez fue un problema, ya que el uso SELECT *..significa " devolver TODAS las columnas ". En general, esto devolverá muchos más datos de los que realmente desea, lo que obviamente puede dar lugar a muchas más E / S, tanto de disco como de red.

Lo que es menos obvio es que esto también restringe los índices y planes de consulta que puede usar un optimizador de SQL, porque sabe que finalmente debe devolver todas las columnas de datos. Si puede saber de antemano que solo desea ciertas columnas, entonces a menudo puede usar planes de consulta más eficientes aprovechando los índices que solo tienen esas columnas. Afortunadamente, hay una manera de saber esto con anticipación, que es para que usted especifique explícitamente las columnas que desea en la lista de columnas. Pero cuando usas "*", estás renunciando a esto a favor de "solo dame todo, descubriré lo que necesito".

Sí, también hay un uso adicional de CPU y memoria para procesar cada columna, pero casi siempre es menor en comparación con estas dos cosas: el disco adicional significativo y el ancho de banda de red requerido para las columnas que no necesita, y tener que usar menos plan de consulta optimizado porque tiene que incluir cada columna.

Entonces, ¿qué cambió? Básicamente, los optimizadores de SQL incorporaron con éxito una característica llamada "Optimización de columna" que solo significa que ahora pueden averiguar en las subconsultas de nivel inferior si alguna vez va a utilizar una columna en los niveles superiores de la consulta.

El resultado de esto es que ya no importa si usa 'SELECCIONAR * ...' en los niveles inferior / interno de una consulta. En cambio, lo que realmente importa es lo que está en la lista de columnas del SELECT de nivel superior. A menos que lo use SELECT *..en la parte superior, una vez más, debe suponer que desea TODAS las columnas y, por lo tanto, no puede emplear las optimizaciones de columna de manera efectiva.

(* - tenga en cuenta que hay un problema de enlace menor y diferente en las Vistas *donde no siempre registran el cambio en las listas de columnas cuando se usa "*". Hay otras formas de abordar esto y no afecta el rendimiento).


5

Hay una pequeña razón más para no usarla SELECT *: si el orden de las columnas devuelve cambios, su aplicación se romperá ... si tiene suerte. Si no lo está, tendrá un error sutil que podría pasar desapercibido durante mucho tiempo. El orden de los campos en una tabla es un detalle de implementación que las aplicaciones nunca deben tener en cuenta, ya que el único momento en que es visible es si utiliza a SELECT *.


44
Esto es irrelevante. Si está accediendo a las columnas por el índice de columna en el código de su aplicación, entonces merece tener una aplicación rota. Acceder a las columnas por nombre siempre produce un código de aplicación mucho más legible y casi nunca es el cuello de botella en el rendimiento.
Lie Ryan

3

Se permite su uso físico y problemático select * from table, sin embargo, es una mala idea. ¿Por qué?

En primer lugar, encontrará que está devolviendo columnas que no necesita (recursos pesados).

En segundo lugar, tomará más tiempo en una tabla grande que nombrar las columnas porque cuando selecciona *, en realidad está seleccionando los nombres de columna de la base de datos y diciendo "dame los datos que están asociados con columnas que tienen nombres en esta otra lista ". Si bien esto es rápido para el programador, imagine hacer esta búsqueda en la computadora de un banco que podría tener literalmente cientos de miles de búsquedas en un minuto.

En tercer lugar, hacer esto realmente hace que sea más difícil para el desarrollador. ¿Con qué frecuencia necesita cambiar de SSMS a VS para obtener todos los nombres de columna?

En cuarto lugar, es una señal de programación perezosa y no creo que ningún desarrollador quiera esa reputación.


Su segundo argumento en esta forma actual tiene algunos pequeños errores. Primero, todos los RDBMS almacenan en caché el esquema de las tablas, principalmente porque el esquema se cargará de todos modos en la etapa de análisis de consultas para determinar qué columna existe o falta en la tabla de la consulta. Por lo tanto, el analizador de consultas ya consultó la lista de nombres de columna por sí solo, e instantáneamente reemplaza * con una lista de las columnas. Entonces, la mayoría de los motores RDBMS intentan almacenar en caché todo lo que puede, por lo que si emite la tabla SELECT * FROM, la consulta compilada se almacenará en caché para que el análisis no ocurra siempre. Y los desarrolladores son flojos :-)
Gabor Garami

Con respecto a su segundo argumento, este es un error común: el problema con SELECT * no es la búsqueda de metadatos, ya que si nombra las columnas, SQL Server todavía tiene que validar sus nombres, verificar los tipos de datos, etc.
Aaron Bertrand

@Gabor Uno de los problemas con SELECT * ocurre cuando lo pones en una vista. Si cambia el esquema subyacente, la vista puede confundirse; ahora tiene un concepto diferente del esquema de la tabla (propio) que la tabla misma. Hablo de esto aquí .
Aaron Bertrand

3

Puede ser un problema si coloca el Select * ...código en un programa, porque, como se señaló anteriormente, la base de datos puede cambiar con el tiempo y tener más columnas de las que esperaba cuando escribió la consulta. Esto puede conducir a la falla del programa (el mejor de los casos) o el programa podría seguir su camino feliz y corromper algunos datos porque está buscando valores de campo que no se escribieron para manejar. En resumen, el código de producción SIEMPRE debe especificar los campos que se devolverán en SELECT.

Dicho esto, tengo menos problemas cuando Select *es parte de una EXISTScláusula, ya que todo lo que se devolverá al programa es un booleano que indica el éxito o el fracaso de la selección. Otros pueden estar en desacuerdo con esta postura y respeto su opinión al respecto. PUEDE ser un poco menos eficiente codificar Select *que codificar 'Seleccionar 1' en una EXISTScláusula, pero no creo que haya ningún peligro de corrupción de datos, de cualquier manera.


En realidad, sí, tenía la intención de hacer referencia a la cláusula EXISTS. Mi error.
Mark Ross

2

Muchas respuestas por qué select *está mal, así que lo cubriré cuando sienta que es correcto o al menos correcto.

1) En EXISTS, el contenido de la parte SELECT de la consulta se ignora, por lo que incluso puede escribir SELECT 1/0y no generará errores. EXISTSsolo verifica que algunos datos regresarían y devuelve un valor booleano basado en eso.

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2) Esto podría iniciar una tormenta de fuego, pero me gusta usar los select *desencadenantes de mi tabla de historial. Por select *, evita que la tabla principal obtenga una nueva columna sin agregar la columna a la tabla del historial, ya que genera un error inmediatamente cuando se inserta / actualiza / elimina en la tabla principal. Esto ha impedido en numerosas ocasiones que los desarrolladores agreguen columnas y olviden agregarlo a la tabla del historial.


3
Todavía prefiero SELECT 1porque obviamente notifica a los futuros mantenedores de código de su intención. No es un requisito , pero si lo veo ... WHERE EXISTS (SELECT 1 ...), obviamente se anuncia como una prueba de verdad.
swasheck

1
@zlatanMuchas personas usan SELECT 1según el mito de que el rendimiento sería mejor que SELECT *. Sin embargo, ambas opciones son perfectamente aceptables. No hay diferencia en el rendimiento debido a la forma en que el optimizador maneja EXISTS. Tampoco hay diferencia en la legibilidad debido a la palabra "EXISTE" que anuncia claramente una prueba de verdad.
Desilusionado el

En el punto # 2, entiendo su razonamiento, pero todavía hay riesgos. Déjame "pintar un escenario para ti" ... El desarrollador agrega Column8a la tabla principal olvidando la tabla de historial. El desarrollador escribe un montón de código relacionado con la Columna 8. Luego agrega Column9a la tabla principal; esta vez recordando agregar también a la historia. Más tarde, cuando realiza las pruebas, se da cuenta de que olvidó agregar Column9al historial (gracias a su técnica de detección de errores) y lo agrega rápidamente. Ahora el disparador parece funcionar, pero los datos en las columnas 8 y 9 están mezclados en el historial. : S
Desilusionado

cont ... El punto es que el escenario 'inventado' anterior es solo uno de los muchos que podría provocar que su truco de detección de errores le falle y, en realidad, empeore las cosas. Básicamente necesitas una mejor técnica. Uno que no se base en su disparador haciendo suposiciones sobre el orden de las columnas en una tabla que seleccione. Sugerencias: - Revisiones de códigos personales con listas de verificación de sus errores comunes. - Revisiones de código de pares. - Técnica alternativa para el seguimiento del historial (personalmente considero que los mecanismos basados ​​en disparadores son reactivos en lugar de proactivos y, por lo tanto, propensos a errores).
Desilusionado

@CraigYoung Esa es una posibilidad. Pero estrangularía a alguien si hiciera eso. Eso no es un error que podría cometer fácilmente
UnhandledExcepSean
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.