Ayer estaba discutiendo con un programador de "pasatiempos" (yo mismo soy un programador profesional). Nos encontramos con parte de su trabajo, y dijo que siempre consulta todas las columnas de su base de datos (incluso en / en el servidor / código de producción).
Traté de convencerlo de que no lo hiciera, pero aún no tuve tanto éxito. En mi opinión, un programador solo debe consultar lo que realmente se necesita en aras de la "belleza", la eficiencia y el tráfico. ¿Estoy equivocado con mi punto de vista?
Respuestas:
Piense en lo que está obteniendo y cómo los vincula a las variables en su código.
Ahora piense qué sucede cuando alguien actualiza el esquema de la tabla para agregar (o eliminar) una columna, incluso una que no esté usando directamente.
Usar select * cuando está escribiendo consultas a mano está bien, no cuando está escribiendo consultas para el código.
fuente
Cambios de esquema
foo
, y otra tabla en la consulta agrega una columnafoo
, la forma en que se maneja esto puede causar problemas al intentar obtener la columna correctafoo
.De cualquier manera, un cambio de esquema puede causar problemas con la extracción de los datos.
Considere además si una columna que se estaba utilizando se elimina de la tabla. El
select * from ...
sigue funcionando pero los errores a cabo al tratar de extraer los datos del conjunto de resultados. Si la columna se especifica en la consulta, la consulta generará un error en lugar de dar una clara indicación de qué y dónde está el problema.Sobrecarga de datos
Algunas columnas pueden tener una cantidad significativa de datos asociada a ellas. Al volver a seleccionar,
*
se extraerán todos los datos. Sí, esto esvarchar(4096)
eso en 1000 filas que ha seleccionado de nuevo, lo que le proporciona 4 megabytes de datos adicionales posibles que no necesita, pero de todos modos se envía a través del cable.En relación con el cambio de esquema, es posible que varchar no exista allí cuando creó la tabla por primera vez, pero ahora está allí.
Falta de transmitir intención
Cuando selecciona de nuevo
*
y obtiene 20 columnas pero solo necesita 2 de ellas, no está transmitiendo la intención del código. Al mirar la consulta que se hace,select *
uno no sabe cuáles son las partes importantes de la misma. ¿Puedo cambiar la consulta para usar este otro plan en lugar de hacerlo más rápido al no incluir estas columnas? No lo sé porque la intención de lo que devuelve la consulta no está clara.Veamos algunos violines SQL que exploran esos cambios de esquema un poco más.
Primero, la base de datos inicial: http://sqlfiddle.com/#!2/a67dd/1
DDL:
SQL:
Y las columnas que vuelvas son
oneid=1
,data=42
,twoid=2
, yother=43
.Ahora, ¿qué sucede si agrego una columna a la tabla uno? http://sqlfiddle.com/#!2/cd0b0/1
Y mis resultados de la misma consulta que antes son
oneid=1
,data=42
,twoid=2
, yother=foo
.Un cambio en una de las tablas altera los valores de a
select *
y de repente su vinculación de 'otro' a un int arrojará un error y no sabe por qué.Si en cambio su declaración SQL fue
El cambio a la tabla uno no habría interrumpido sus datos. Esa consulta se ejecuta igual antes del cambio y después del cambio.
Indexación
Cuando haces un,
select * from
estás tirando de todas las filas de todas las tablas que coinciden con las condiciones. Incluso mesas que realmente no te importan. Si bien esto significa que se transfieren más datos, hay otro problema de rendimiento que acecha más abajo en la pila.Índices (relacionado con SO: ¿Cómo usar el índice en la instrucción select? )
Si está retirando muchas columnas, el optimizador del plan de la base de datos puede ignorar el uso de un índice porque de todos modos tendrá que recuperar todas esas columnas y tomará más tiempo usar el índice y luego recuperar todas las columnas en la consulta de lo que sería hacer un escaneo completo de la tabla.
Si solo está seleccionando, por ejemplo, el apellido de un usuario (que hace mucho y por lo tanto tiene un índice), la base de datos puede hacer un escaneo de solo índice (escaneo de índice wiki de postgres solo , escaneo de tabla completa de mysql vs completo) exploración de índice , exploración de solo índice: evitar el acceso a la tabla ).
Hay bastantes optimizaciones sobre la lectura solo de índices si es posible. La información se puede extraer más rápido en cada página de índice porque también está extrayendo menos; no está extrayendo todas esas otras columnas para el
select *
. Es posible que un escaneo de solo índice arroje resultados del orden de 100 veces más rápido (fuente: Seleccionar * es incorrecto ).Esto no quiere decir que una exploración de índice completa sea excelente, sigue siendo una exploración completa, pero es mejor que una exploración de tabla completa. Una vez que empiezas a perseguir todas las formas que
select *
perjudican el rendimiento, sigues encontrando nuevas.Lectura relacionada
fuente
select *
?Otra preocupación: si se trata de una
JOIN
consulta y está recuperando los resultados de la consulta en una matriz asociativa (como podría ser el caso en PHP), es propensa a errores.La cosa es que
foo
tiene columnasid
yname
bar
tiene columnasid
yaddress
,SELECT * FROM foo JOIN bar ON foo.id = bar.id
adivina qué sucede cuando alguien agrega una columna
name
a labar
tabla.El código repentinamente dejará de funcionar correctamente, porque ahora la
name
columna aparece en los resultados dos veces y si está almacenando los resultados en una matriz, los datos de secondname
(bar.name
) sobrescribirán el primeroname
(foo.name
).Es un error bastante desagradable porque no es muy obvio. Puede tomar un tiempo darse cuenta, y no hay forma de que la persona que agrega otra columna a la tabla haya anticipado un efecto secundario tan indeseable.
(Historia verdadera).
Por lo tanto, no use
*
, controle qué columnas está recuperando y use alias cuando corresponda.fuente
SELECT
cláusula y esto es cuando esperemos que el nombre no sea único. Por cierto, no creo que sea tan raro en sistemas con grandes bases de datos. Como dije, una vez pasé un par de horas buscando este error en una gran bola de barro de código PHP. Y encontré otro caso justo ahora: stackoverflow.com/q/17715049/168719Consultar cada columna puede ser perfectamente legítimo, en muchos casos.
Siempre consultar cada columna no lo es.
Es más trabajo para su motor de base de datos, que tiene que explotar y revolver alrededor de sus metadatos internos para determinar con qué columnas debe lidiar antes de poder continuar con el negocio real de obtener los datos y enviárselos. De acuerdo, no es la mayor sobrecarga del mundo, pero los catálogos de sistemas pueden ser un cuello de botella apreciable.
Es más trabajo para su red, porque está retirando cualquier número de campos cuando solo desea uno o dos de ellos. Si alguien [más] va y agrega un par de docenas de campos adicionales, todos los cuales contienen grandes fragmentos de texto, su rendimiento de repente pasa por el suelo, sin razón aparente. Esto empeora si su cláusula "dónde" no es particularmente buena y está retirando muchas filas también; eso es potencialmente una gran cantidad de datos que se abren paso a través de la red hacia usted (es decir, va a ser lento).
Es más trabajo para su aplicación, tener que retroceder y almacenar todos estos datos adicionales que probablemente no le importen.
Corre el riesgo de que las columnas cambien su orden. OK, no debería tener que preocuparse por esto (y no lo hará si selecciona solo las columnas que necesita) pero, si las obtiene todas de una vez y alguien [más] decide reorganizar el orden de las columnas dentro de la tabla , esa exportación de CSV cuidadosamente elaborada que le da a las cuentas del pasillo de repente se va a la basura, nuevamente, sin ninguna razón aparente.
Por cierto, he dicho "alguien [más]" un par de veces, arriba. Recuerde que las bases de datos son inherentemente multiusuario; Es posible que no tenga el control sobre ellos que cree que tiene.
fuente
TOP
limitación; No estoy seguro de lo importante que es si el código lee todo lo que le interesa mostrar y luego elimina la consulta. Creo que las respuestas a las consultas se procesan de manera un tanto perezosa, aunque no conozco los detalles. En cualquier caso, creo que en lugar de decir que "no es legítimo", sería mejor decir "... es legítimo en muchos menos"; básicamente, resumiría los casos legítimos como aquellos en los que el usuario tendría una mejor idea de lo que es significativo que el programador.La respuesta corta es: depende de qué base de datos usen. Las bases de datos relacionales están optimizadas para extraer los datos que necesita de una manera rápida, confiable y atómica . En grandes conjuntos de datos y consultas complejas, es mucho más rápido y probablemente más seguro que SELECTing * y hace el equivalente de las uniones en el lado del "código". Las tiendas de valores clave pueden no tener implementadas tales funcionalidades, o pueden no ser lo suficientemente maduras como para usarlas en la producción.
Dicho esto, aún puede completar cualquier estructura de datos que esté utilizando con SELECT * y resolver el resto en el código, pero encontrará cuellos de botella de rendimiento si desea escalar.
La comparación más cercana es la clasificación de datos: puede usar quicksort o bubbleort y el resultado será correcto. Pero no se optimizará, y definitivamente tendrá problemas cuando introduzca concurrencia y necesite ordenar atómicamente.
Por supuesto, es más barato agregar RAM y CPU que invertir en un programador que pueda hacer consultas SQL e incluso tenga una vaga comprensión de lo que es JOIN.
fuente
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
Vea Time to Take Ofnsense en la página 2.var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
... y luego procede a crear un Cliente a partir de cada fila. LINQ le quita los pantalones a eso.var customer = _db.Customers.Where(it => it.id == id).First();
.OMI, se trata de ser explícito vs implícito. Cuando escribo código, quiero que funcione porque lo hice funcionar, no solo porque todas las partes están allí. Si consulta todos los registros y su código funciona, entonces tendrá la tendencia a seguir adelante. Más adelante, si algo cambia y ahora su código no funciona, es un verdadero problema depurar muchas consultas y funciones en busca de un valor que debería estar allí y los únicos valores de referencia son *.
También en un enfoque de N niveles, es mejor aislar las interrupciones del esquema de la base de datos en el nivel de datos. Si su nivel de datos pasa * a la lógica de negocios y probablemente en el nivel de presentación, está expandiendo su alcance de depuración exponencialmente.
fuente
select *
es mucho peor!porque si la tabla obtiene nuevas columnas, entonces obtienes todas esas, incluso cuando no las necesitas. con
varchars
esto puede convertirse en una gran cantidad de datos adicionales que deben viajar desde el DBalgunas optimizaciones de base de datos también pueden extraer los registros de longitud no fija en un archivo separado para acelerar el acceso a las partes de longitud fija, usando select * derrota el propósito de ese
fuente
Además de los gastos generales, algo que desea evitar en primer lugar, diría que, como programador, no depende del orden de las columnas definido por el administrador de la base de datos. Selecciona cada columna incluso si las necesitas todas.
fuente
No veo ninguna razón por la que no deba usar para el propósito de su compilación: recupere todas las columnas de una base de datos. Veo tres casos:
Se agrega una columna en la base de datos y también la quiere en el código. a) Con * fallará con un mensaje apropiado. b) Sin * funcionará, pero no hará lo que espera, lo cual es bastante malo.
Se agrega una columna en la base de datos y no la desea en el código. a) Con * fallará; esto significa que * ya no se aplica, ya que su semántica significa "recuperar todo". b) Sin * funcionará.
Se elimina una columna El código fallará de cualquier manera.
Ahora el caso más común es el caso 1 (ya que usó *, lo que significa que probablemente lo desee todo); sin * puede tener un código que funciona bien pero no hace lo que se esperaba, que es mucho, mucho peor, ese código que falla con un mensaje de error adecuado .
No estoy teniendo en cuenta el código que recupera los datos de la columna en función del índice de la columna que, en mi opinión, es propenso a errores. Es mucho más lógico recuperarlo en función del nombre de la columna.
fuente
Select *
se pensó más como una conveniencia para consultas ad-hoc, no para propósitos de desarrollo de aplicaciones. O para usar en construcciones estadísticas como laselect count(*)
que permite que el motor de consultas decida si usar un índice, qué índice usar, etc., y no está devolviendo ningún dato de columna real. O para usar en cláusulas comowhere exists( select * from other_table where ... )
, que nuevamente es una invitación al motor de consultas para elegir la ruta más eficiente por sí misma y la subconsulta solo se usa para restringir los resultados de la consulta principal. Etc.select *
tiene la semántica de recuperar todas las columnas; Si su aplicación realmente necesita esto, no veo ninguna razón por la que no lo use. ¿Puede señalar alguna referencia (Oracle, IBM, Microsoft, etc.) que mencione que el propósito para el queselect *
se construyó no es recuperar todas las columnas?select *
existe para recuperar todas las columnas ... como una característica conveniente, para consultas ad-hoc, no porque sea una gran idea en el software de producción. Las razones ya están bastante bien cubiertas en las respuestas de esta página, por lo que no creé mi propia respuesta detallada: •) Problemas de rendimiento, organización repetida de datos en la red que nunca usa, •) problemas con el alias de columna, •) fallas de optimización del plan de consulta (falla en el uso de índices en algunos casos), •) E / S de servidor ineficientes en casos donde la selección limitada podría haber usado únicamente índices, etc.select *
en una aplicación de producción real, pero la naturaleza de un caso límite es que no es el caso común . :-)select *
; lo que estaba diciendo si realmente necesita todas las columnas, no veo ninguna razón por la que no deba usarloselect *
; aunque pocos deben existir escenarios en los que se necesiten todas las columnas.Piénselo de esta manera ... si consulta todas las columnas de una tabla que tiene solo una pequeña cadena o campos numéricos, eso totaliza 100k de datos. Mala práctica, pero funcionará. Ahora agregue un solo campo que contenga, por ejemplo, una imagen o un documento de 10 MB de palabras. ahora su consulta de rendimiento rápido comienza de manera misteriosa de inmediato y de manera deficiente, solo porque se agregó un campo a la tabla ... es posible que no necesite ese gran elemento de datos, pero debido a que lo ha hecho,
Select * from Table
lo obtiene de todos modos.fuente