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

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

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

Incluso teniendo en count la pregunta * Consulta SQL – Seleccione * de la vista o Seleccione col1, col2, … colN de la vista *, no creo que 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 esto en mente, parece que el uso de SELECT * debería ser el método preferido hasta que se demuestre que es un problema de resources o el esquema está prácticamente grabado. Que, como sabemos, no ocurrirá hasta que el desarrollo esté completo.

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

La esencia de la cita de optimization no prematura es ir por un código simple y directo y luego utilizar un generador de perfiles para señalar los puntos calientes, que luego puede optimizar para ser eficiente.

Cuando usa select *, hace que sea imposible hacer 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.


Entonces, seleccionar columnas no es una optimization prematura. Algunas cosas fuera de mi cabeza …

  1. Si especifica columnas en una statement SQL, el motor de ejecución de SQL generará 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 está utilizando 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 seleccionar *
  5. Si su instrucción de SQL une tablas, select * le da todas las columnas de todas las tablas en la combinación

El corolario es que usar select *

  1. Las columnas utilizadas por la aplicación son opacas
  2. Los DBA y sus perfiles de consulta no pueden ayudar al performance deficiente de su aplicación
  3. El código es más quebradizo cuando ocurren cambios
  4. Su database y su networking están sufriendo porque están devolviendo demasiados datos (E / S)
  5. Las optimizaciones de motor de database son mínimas ya que está recuperando todos los datos independientemente (lógico).

Escribir SQL correctamente es tan fácil como escribir Select * . Entonces, la persona realmente perezosa escribe el SQL apropiado porque no quiere volver a visitar el código e intentar recordar lo que estaba haciendo cuando lo hizo. No quieren explicarle a los DBA todo el código. No quieren explicarles a sus clientes por qué la aplicación funciona como un perro.

Si su código depende de que las columnas estén en un order específico, su código se romperá cuando haya cambios en la tabla. Además, es posible que obtenga demasiado de la tabla cuando selecciona *, especialmente si hay un campo binary en la tabla.

El hecho de que esté usando 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 caching de la ejecución del plan, ya que tiene que search los metadatos sobre la tabla para saber qué columnas están en *.

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

  1. De una manera indirecta, usted está rompiendo la regla de modularidad sobre el uso de tipeo estricto siempre que sea posible. Explícito es casi universalmente mejor.

  2. Incluso si ahora necesita todas las columnas de la tabla, más podría agregarse más tarde, lo que se networkingucirá cada vez que ejecute la consulta y podría perjudicar el performance. Duele performance porque

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

Cuando se usa seleccionar *

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

Hay unas pocas razones:

  1. Si cambia el número de columnas en una database y su aplicación espera que haya un cierto número …
  2. Si el order de las columnas en una database cambia y su aplicación espera que estén en cierto order …
  3. Sobrecarga de memory. 8 columnas INTEGER innecesarias agregarían 32 bytes de memory desperdiciada. Eso no suena como mucho, pero esto es para cada consulta e INTEGER es uno de los types de columnas pequeñas … las columnas adicionales son más propensas a ser columnas VARCHAR o TEXT, que se sumn más rápido.
  4. Sobrecarga de networking Relacionado con la sobrecarga de memory: si publico 30,000 consultas y tengo 8 columnas INTEGER innecesarias, desperdicié 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.

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

Dicho esto, hay una serie de situaciones en las que SELECT * es deseable. Una es una situación que encuentro todo el time, donde necesito replicar una tabla completa en otra database (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).

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

Ejecute la siguiente consulta y verá a qué me refiero.

 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 seleccionadas. Creo que lo que verá será el resultado de seleccionar * columnas de reference por índice en lugar de nombre.

Si reconstruyes la vista, funcionará bien de nuevo.

EDITAR

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

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 un significado diferente), lo más probable es que obtenga los valores de la primera tabla y no 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 de selección. Si la nueva columna no es necesaria cada vez que se ejecuta la consulta, puede tomar un time antes de que el error se note.

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

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

Cuando especifica columnas, también se está atando a un grupo específico de columnas y haciéndose less flexible, haciendo que Feuerstein se transfiera, bueno, donde sea que esté. Solo un pensamiento.

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

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

Un ejemplo:

  • una tabla de lugares, con coorderadas almacenadas en los campos labeldos x, y, z:

    CREATE TABLE places (place_id integer, x numérico (10, 3), y numérico (10, 3), z numérico (10, 3), descripción varchar);

  • vamos a alimentarlo con algunos valores de ejemplo:

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

  • Quiero poder mapear el contenido de esta tabla, usando algún cliente GIS. La forma normal es agregar un campo de geometry a la tabla y build la geometry, en function de las coorderadas. Pero preferiría get una consulta dinámica: de esta manera, cuando cambio las coorderadas (correcciones, más precisión, etc.), los objects asignados se mueven realmente de forma dinámica. Así que aquí está la consulta con el SELECT * :

    CREAR O REEMPLAZAR VER places_points AS
    SELECCIONE *,
    GeomFromewkt ('SRID = 4326; POINT (' || x || '' || y || '' || z || ')')
    DESDE lugares;

    Consulte postgis, para el uso de la function GeomFromewkt ().

  • Aquí está el resultado:

    SELECCIONAR * FROM places_points;

  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 más a la derecha ahora puede ser utilizada por cualquier progtwig SIG para mapear adecuadamente los puntos.

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

Deseo que la definición de la VISTA pueda mantenerse "tal como está", con el *, pero hélas no es el caso: así es como se almacena internamente por postgresql:

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

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

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

Seleccionar solo las columnas que necesita mantiene el set de datos en la memory más pequeño y, por lo tanto, mantiene su aplicación más rápida.

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

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 no le importe).

Para responder su pregunta directamente: No use "SELECCIONAR *" cuando hace que su código sea más confuso a los cambios en las tablas subyacentes. Su código debe romperse solo cuando se realice un cambio en la tabla que afecte directamente a los requisitos de su progtwig.

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

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

En general es malo usar 'seleccionar *' dentro de las vistas porque se verá obligado a recomstackr la vista en caso de que se modifique la columna de la tabla. Al cambiar las columnas de la tabla subyacente de una vista, recibirá un error para las columnas que no existen hasta que vuelva y vuelva a comstackr.

Está bien cuando lo haces exists(select * ...) ya que nunca se expande. De lo contrario, solo es útil cuando se exploran tablas con sentencias de selección temporales o si se ha definido un CTE arriba y se quiere que cada columna no vuelva a tipearlas todas.

Solo para agregar una cosa que nadie más ha mencionado. Select * devuelve todas las columnas, alguien puede agregar una columna más adelante que no necesariamente desea que los usuarios puedan ver, como por ejemplo, quién actualizó la información por última vez o una timestamp o notas que solo los gerentes deben ver, no todos los usuarios, etc.

Además, al agregar una columna, se debe revisar y considerar el impacto en el código existente para ver si se necesitan cambios según la información que se almacena en la columna. Al usar select * , esa revisión a menudo se saltará porque el desarrollador supondrá que nada se romperá. Y, de hecho, es posible que nada parezca que se rompa explícitamente, pero las consultas ahora pueden comenzar a devolver algo incorrecto. El hecho de que nada se rompa explícitamente, no significa que no debieron haber cambios en las consultas.

porque "select *" perderá memory cuando no necesite todos los campos. Pero para el server sql, su performance es el mismo.