¿Por qué las consultas se analizan de tal manera que no se permite el uso de alias de columna en la mayoría de las cláusulas?


16

Mientras intentaba escribir una consulta, descubrí (por las malas) que SQL Server analiza WHEREs en una consulta mucho antes de analizar los SELECT al ejecutar una consulta.

Los documentos de MSDN dicen que el orden de análisis lógico general es tal que SELECT se analiza casi al final (lo que resulta en errores de "no hay tal objeto [alias]" al intentar usar un alias de columna en otras cláusulas). Incluso hubo una sugerencia para permitir el uso de alias en cualquier lugar, que fue rechazado por el equipo de Microsoft, citando problemas de cumplimiento de los estándares ANSI (lo que sugiere que este comportamiento es parte del estándar ANSI).

Como programador (no un DBA), encontré este comportamiento algo confuso, ya que me parece que en gran medida anula el propósito de tener alias de columna (o, al menos, los alias de columna podrían hacerse significativamente más poderosos si fueran analizado anteriormente en la ejecución de la consulta), ya que el único lugar donde puede usar los alias es ORDER BY. Como programador, parece que está perdiendo una gran oportunidad para hacer que las consultas sean más potentes, convenientes y SECAS.

Parece que es un problema tan evidente que es lógico, entonces, que hay otras razones para decidir que los alias de columna no deberían permitirse en otra cosa que no sea SELECT y ORDER BY, pero ¿cuáles son esas razones?

Respuestas:


19

Resumen

No hay una razón lógica para que no se pueda hacer, pero el beneficio es pequeño y hay algunas dificultades que pueden no ser evidentes de inmediato.

Resultados de la investigacion

Investigué un poco y encontré buena información. La siguiente es una cita directa de una fuente primaria confiable (que desea permanecer en el anonimato) al 2012-08-09 17:49 GMT:

Cuando se inventó SQL por primera vez, no tenía alias en la cláusula SELECT. Esta fue una deficiencia grave que se corrigió cuando el lenguaje fue estandarizado por ANSI aproximadamente en 1986.

El lenguaje estaba destinado a ser "no procesal", en otras palabras, para describir los datos que desea sin especificar cómo encontrarlos. Entonces, hasta donde sé, no hay ninguna razón por la cual una implementación de SQL no pueda analizar toda la consulta antes de procesarla, y permitir que los alias se definan en cualquier lugar y se usen en todas partes. Por ejemplo, no veo ninguna razón por la cual la siguiente consulta no debería ser válida:

select name, salary + bonus as pay
from employee
where pay > 100000

Aunque creo que esta es una consulta razonable, algunos sistemas basados ​​en SQL pueden introducir restricciones en el uso de alias por alguna razón relacionada con la implementación. No me sorprende escuchar que SQL Server hace esto.

Estoy interesado en más investigación sobre el estándar SQL-86 y por qué los DBMS modernos no admiten la reutilización de alias, pero aún no he tenido tiempo de llegar muy lejos con él. Para empezar, no sé dónde obtener la documentación o cómo averiguar quién formó exactamente el comité. ¿Alguien puede ayudar? También me gustaría saber más sobre el producto Sybase original del que proviene SQL Server.

A partir de esta investigación y algunas reflexiones adicionales, he llegado a sospechar que el uso de alias en otras cláusulas, aunque es bastante posible, nunca ha sido una prioridad tan alta para los fabricantes de DBMS en comparación con otras características del lenguaje. Dado que no es un gran obstáculo, el redactor de consultas puede evitarlo con facilidad, por lo que no es óptimo esforzarse por otros avances. Además, sería propietario, ya que obviamente no forma parte del estándar SQL (aunque estoy esperando saber más sobre eso con seguridad) y, por lo tanto, sería una mejora menor, rompiendo la compatibilidad SQL entre DBMS. En comparación, CROSS APPLY(que en realidad no es más que una tabla derivada que permite referencias externas) es un gran cambio, que si bien el propietario ofrece un increíble poder expresivo que no se realiza fácilmente de otras maneras.

Problemas con el uso de alias en todas partes

Si permite que los elementos SELECT se coloquen en la cláusula WHERE, no solo puede explotar la complejidad de la consulta (y, por lo tanto, la complejidad de encontrar un buen plan de ejecución), es posible encontrar cosas completamente ilógicas. Tratar:

SELECT X + 5 Y FROM MyTable WHERE Y = X

¿Qué sucede si MyTable ya tiene una columna Y, a cuál se refiere la cláusula WHERE? La solución es usar un CTE o una tabla derivada, que en la mayoría de los casos no debería costar más pero logra el mismo resultado final. Los CTE y las tablas derivadas al menos imponen la resolución de la ambigüedad al permitir que un alias se use solo una vez.

Además, no usar alias en la cláusula FROM tiene mucho sentido. No puedes hacer esto:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

Eso es una referencia circular (en el sentido de que T2 se refiere en secreto a un valor de T3, antes de que la tabla se ha presentado en la lista JOIN), y rematadamente difícil de ver. Que tal este:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

¿Cuánto desea apostar a que la función newid () se incluirá dos veces en el plan de ejecución, haciendo que las dos columnas muestren valores diferentes de forma inesperada? ¿Qué pasa cuando la consulta anterior se utiliza N niveles profundos en CTE o tablas derivadas? Te garantizo que el problema es peor de lo que puedes imaginar. Ya existen serios problemas de inconsistencia sobre cuándo las cosas se evalúan solo una vez o en qué punto de un plan de consulta, y Microsoft ha dicho que no solucionaráalgunos de ellos porque expresan el álgebra de consultas correctamente; si uno obtiene resultados inesperados, divida la consulta en partes. Permitir referencias encadenadas, detectar referencias circulares a través de cadenas potencialmente muy largas, estos son problemas bastante difíciles. Introduce el paralelismo y tendrás una pesadilla en ciernes.

Nota: Usar el alias en WHERE o GROUP BY no va a hacer una diferencia en los problemas con funciones como newid () o rand ().

Una forma de SQL Server para crear expresiones reutilizables

CROSS APPLY / OUTER APPLY es una forma en SQL Server de crear expresiones que se pueden usar en cualquier otro lugar de la consulta (solo que no antes en la cláusula FROM):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Esto hace dos cosas:

  1. Hace que todas las expresiones en CROSS APPLY obtengan un "espacio de nombres" (un alias de tabla, aquí, X) y sean únicas dentro de ese espacio de nombres.
  2. Hace evidente en todas partes, no solo que CalcID proviene de X, sino que también hace evidente por qué no puede usar nada de X cuando se une a la tabla T1 y T3, porque X aún no se ha introducido.

En realidad, soy bastante aficionado a CROSS APPLY. Se ha convertido en mi fiel amigo, y lo uso todo el tiempo. ¿Necesita un UNPIVOT parcial (que requeriría un PIVOT / UNPIVOT o UNPIVOT / PIVOT usando la sintaxis nativa)? Hecho con APLICACIÓN CRUZADA. ¿Necesita un valor calculado que se reutilizará muchas veces? Hecho. ¿Necesita imponer rígidamente la orden de ejecución para las llamadas a través de un servidor vinculado? Hecho, con una mejora en la velocidad de gritos. ¿Necesita solo un tipo de fila dividida en 2 filas o con condiciones adicionales? Hecho.

Por lo menos, en DBMS SQL Server 2005 y versiones posteriores, no tiene más motivos para quejarse: CROSS APPLY es la forma en que SECA de la manera que desea.


14

No puedo decirle las razones exactas, pero le diré que existen soluciones alternativas para repetir expresiones, por ejemplo, usar CTE, subconsultas, tablas derivadas, etc. para evitar la repetición.

Si muestra una consulta con una expresión repetida, probablemente podamos mostrarle cómo volver a escribirla para que la expresión solo aparezca una vez. Sin embargo, esto solo reduce la complejidad de escribir / leer la consulta, es poco probable que cambie mucho la eficiencia. SQL Server generalmente es bastante bueno para reconocer que las expresiones se repiten, y no realizará ese trabajo dos veces. Hay excepciones que van para otro lado, pero solo debe preocuparse por la eficiencia cuando realmente observa que esto sucede. Sospecho que la mayoría de las expresiones repetidas que escribe están realmente colapsadas en una sola operación en el plan.

Dicho todo esto, también repetiré parte de mi respuesta de esta pregunta:

/dba/19762/why-is-the-select-clause-listed-first


Aquí está la explicación de Joe Celko de cómo se procesa una consulta de acuerdo con el estándar (robé esto de mi propio artículo de aspfaq.com , que robó la cita probablemente de una publicación de grupo de noticias de Celko):

Así es como funciona un SELECT en SQL ... al menos en teoría. Los productos reales optimizarán las cosas cuando puedan.

Comience en la cláusula FROM y construya una tabla de trabajo a partir de todas las uniones, uniones, intersecciones y cualquier otro constructor de tablas que esté allí. La opción AS le permite asignar un nombre a esta tabla de trabajo que luego debe usar para el resto de la consulta que lo contiene.

Vaya a la cláusula WHERE y elimine las filas que no pasan criterios; es decir, que no se prueba como VERDADERO (rechazar DESCONOCIDO y FALSO). La cláusula WHERE se aplica al trabajo en la cláusula FROM.

Vaya a la cláusula opcional GROUP BY, haga grupos y reduzca cada grupo a una sola fila, reemplazando la tabla de trabajo original con la nueva tabla agrupada. Las filas de una tabla agrupada deben ser características de grupo: (1) una columna de agrupación (2) una estadística sobre el grupo (es decir, funciones agregadas) (3) una función o (4) una expresión compuesta de esos tres elementos.

Vaya a la cláusula opcional HAVING y aplíquela contra la tabla de trabajo agrupada; Si no hubo una cláusula GROUP BY, trate la tabla completa como un grupo.

Vaya a la cláusula SELECT y construya las expresiones en la lista. Esto significa que las subconsultas escalares, las llamadas a funciones y las expresiones en SELECT se realizan después de que se hacen todas las demás cláusulas. El operador AS también puede asignar un nombre a las expresiones en la lista SELECT. Estos nuevos nombres comienzan a existir de una vez, pero después de que se haya ejecutado la cláusula WHERE; no puede usarlos en la lista SELECCIONAR o en la ubicación de WHERE por ese motivo.

Las expresiones de consulta anidadas siguen las reglas de alcance habituales que esperaría de un lenguaje estructurado en bloques como C, Pascal, Algol, etc. Es decir, las consultas más internas pueden hacer referencia a columnas y tablas en las consultas en las que están contenidas.

Esto significa que un SELECT no puede tener más columnas que un GROUP BY; pero ciertamente puede tener menos columnas.

Ahora, Celko fue uno de los principales contribuyentes a las versiones anteriores de los estándares. No sé si alguna vez vas a obtener una respuesta definitiva a la WHY?pregunta, excepto por la especulación. Supongo que enumerar la operación real primero hace que sea muy fácil para el analizador saber exactamente cuál será el tipo de operación. Imagine una combinación de 20 tablas que podría terminar siendo una SELECTo UPDATEo DELETE, y recuerde que el código para estos motores se escribió originalmente en los días en que el análisis de cadenas era bastante costoso.

Tenga en cuenta que si el estándar SQL dictamina FROMque es lo primero, los proveedores pueden haber decidido analizar la gramática de forma independiente en un orden diferente, por lo que puede que no tenga sentido esperar que el orden de las cláusulas esté escrito para obedecer completamente el orden de procesamiento del 100% de el tiempo.

Lo mismo es cierto para cosas como CASE. Hemos visto escenarios aquí en este sitio , por ejemplo, donde el mito previamente creído que CASEsiempre se procesa en orden y cortocircuitos, es falso. Y esto también se extiende a otras creencias comunes, como la evaluación de las uniones de SQL Server en el orden en que fueron escritas, las WHEREcláusulas de cortocircuito de izquierda a derecha o el procesamiento de CTE una vez o en un cierto orden, incluso si se hace referencia a ellas varias veces. Los productos son libres de optimizar cómo les parezca, incluso si no refleja exactamente cómo ha declarado que la consulta debería funcionar de manera declarativa.


2
También tenga en cuenta que la capacidad de usar o no usar alias en diferentes partes de la consulta es aplicada por el analizador, no por el optimizador o el motor de ejecución. La forma en que el motor realmente ejecuta la consulta no necesariamente refleja las restricciones que afectan la sintaxis.
Aaron Bertrand

2

En Entity SQL , PUEDE usar alias de expresiones en otros lugares de la consulta en algunas situaciones:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Tenga en cuenta que aquí DEBE definir la expresión en la GROUP BYcláusula para poder usarla en la SELECTcláusula.

Obviamente, es posible permitir algo de este tipo de expresión de alias como reutilizable en consultas SQL.

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.