variable uso update into inserted sql logic complexity-theory reduction

uso - update output sql server 2008



Reglas generales para simplificar declaraciones SQL (8)

Estoy buscando algunas "reglas de inferencia" (similares a establecer reglas de operación o reglas lógicas) que puedo usar para reducir una consulta SQL en complejidad o tamaño. ¿Existe algo así? Cualquier documento, alguna herramienta? Cualquier equivalencia que haya encontrado por su cuenta? De alguna manera es similar a la optimización de consultas, pero no en términos de rendimiento.

Para decirlo diferente: Tener una consulta (compleja) con JOINs, SUBSELECTs, UNIONs ¿es posible (o no) reducirla a una declaración SQL más simple y equivalente, que está produciendo el mismo resultado, mediante el uso de algunas reglas de transformación?

Por lo tanto, estoy buscando transformaciones equivalentes de sentencias de SQL, como el hecho de que la mayoría de los SUBSELECTOS pueden reescribirse como un JOIN.


Para decirlo diferente: Tener una consulta (compleja) con JOINs, SUBSELECTs, UNIONs ¿es posible (o no) reducirla a una declaración SQL más simple y equivalente, que está produciendo el mismo resultado, mediante el uso de algunas reglas de transformación?

Eso es exactamente lo que hacen los optimizadores para ganarse la vida (no es que yo diga que siempre lo hacen bien).

Como SQL es un lenguaje basado en conjuntos, generalmente hay más de una forma de transformar una consulta en otra.

Como esta consulta:

SELECT * FROM mytable WHERE col1 > @value1 OR col2 < @value2

se puede transformar en esto:

SELECT * FROM mytable WHERE col1 > @value1 UNION SELECT * FROM mytable WHERE col2 < @value2

o esto:

SELECT mo.* FROM ( SELECT id FROM mytable WHERE col1 > @value1 UNION SELECT id FROM mytable WHERE col2 < @value2 ) mi JOIN mytable mo ON mo.id = mi.id

, que se ven más feos pero pueden producir mejores planes de ejecución.

Una de las cosas más comunes para hacer es reemplazar esta consulta:

SELECT * FROM mytable WHERE col IN ( SELECT othercol FROM othertable )

Con este:

SELECT * FROM mytable mo WHERE EXISTS ( SELECT NULL FROM othertable o WHERE o.othercol = mo.col )

En algunos RDBMS (como PostgreSQL ), DISTINCT y GROUP BY utilizan los diferentes planes de ejecución, por lo que a veces es mejor reemplazar uno con el otro:

SELECT mo.grouper, ( SELECT SUM(col) FROM mytable mi WHERE mi.grouper = mo.grouper ) FROM ( SELECT DISTINCT grouper FROM mytable ) mo

vs.

SELECT mo.grouper, SUM(col) FROM mytable GROUP BY mo.grouper

En PostgreSQL , DISTINCT ordena y hash GROUP BY .

MySQL carece de FULL OUTER JOIN , por lo que puede reescribirse como sigue:

SELECT t1.col1, t2.col2 FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.id = t2.id

vs.

SELECT t1.col1, t2.col2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.id = t2.id UNION ALL SELECT NULL, t2.col2 FROM table1 t1 RIGHT JOIN table2 t2 ON t1.id = t2.id WHERE t1.id IS NULL

, pero vea este artículo en mi blog sobre cómo hacer esto de manera más eficiente en MySQL :

Esta consulta jerárquica en Oracle :

SELECT DISTINCT(animal_id) AS animal_id FROM animal START WITH animal_id = :id CONNECT BY PRIOR animal_id IN (father, mother) ORDER BY animal_id

se puede transformar a esto:

SELECT DISTINCT(animal_id) AS animal_id FROM ( SELECT 0 AS gender, animal_id, father AS parent FROM animal UNION ALL SELECT 1, animal_id, mother FROM animal ) START WITH animal_id = :id CONNECT BY parent = PRIOR animal_id ORDER BY animal_id

, el último siendo más eficiente.

Vea este artículo en mi blog para los detalles del plan de ejecución:

Para encontrar todos los rangos que se superponen con el rango dado, puede usar la siguiente consulta:

SELECT * FROM ranges WHERE end_date >= @start AND start_date <= @end

, pero en SQL Server esta consulta más compleja produce los mismos resultados más rápidamente:

SELECT * FROM ranges WHERE (start_date > @start AND start_date <= @end) OR (@start BETWEEN start_date AND end_date)

, y créanlo o no, tengo un artículo en mi blog sobre esto también:

SQL Server también carece de una forma eficiente de hacer agregados acumulativos, por lo que esta consulta:

SELECT mi.id, SUM(mo.value) AS running_sum FROM mytable mi JOIN mytable mo ON mo.id <= mi.id GROUP BY mi.id

puede ser reescrito más eficientemente usando, Señor ayúdame, cursores (me escuchaste bien: cursors , more efficiently y SQL Server en una oración).

Vea este artículo en mi blog sobre cómo hacerlo:

Existe un cierto tipo de consulta comúnmente encontrada en aplicaciones financieras que busca la tasa efectiva para una moneda, como esta en Oracle :

SELECT TO_CHAR(SUM(xac_amount * rte_rate), ''FM999G999G999G999G999G999D999999'') FROM t_transaction x JOIN t_rate r ON (rte_currency, rte_date) IN ( SELECT xac_currency, MAX(rte_date) FROM t_rate WHERE rte_currency = xac_currency AND rte_date <= xac_date )

Esta consulta puede ser reescrita en gran medida para utilizar una condición de igualdad que permite un HASH JOIN lugar de NESTED LOOPS :

WITH v_rate AS ( SELECT cur_id AS eff_currency, dte_date AS eff_date, rte_rate AS eff_rate FROM ( SELECT cur_id, dte_date, ( SELECT MAX(rte_date) FROM t_rate ri WHERE rte_currency = cur_id AND rte_date <= dte_date ) AS rte_effdate FROM ( SELECT ( SELECT MAX(rte_date) FROM t_rate ) - level + 1 AS dte_date FROM dual CONNECT BY level <= ( SELECT MAX(rte_date) - MIN(rte_date) FROM t_rate ) ) v_date, ( SELECT 1 AS cur_id FROM dual UNION ALL SELECT 2 AS cur_id FROM dual ) v_currency ) v_eff LEFT JOIN t_rate ON rte_currency = cur_id AND rte_date = rte_effdate ) SELECT TO_CHAR(SUM(xac_amount * eff_rate), ''FM999G999G999G999G999G999D999999'') FROM ( SELECT xac_currency, TRUNC(xac_date) AS xac_date, SUM(xac_amount) AS xac_amount, COUNT(*) AS cnt FROM t_transaction x GROUP BY xac_currency, TRUNC(xac_date) ) JOIN v_rate ON eff_currency = xac_currency AND eff_date = xac_date

A pesar de ser voluminoso como un infierno, la última consulta es 6 veces más rápida.

La idea principal aquí es reemplazar <= con = , lo que requiere construir una tabla de calendario en memoria. para JOIN a.


Aquí hay algunos de trabajar con Oracle 8 y 9 (por supuesto, a veces hacer lo contrario podría hacer la consulta más simple o más rápida):

Los paréntesis se pueden eliminar si no se usan para anular la precedencia del operador. Un ejemplo simple es cuando todos los operadores booleanos en su cláusula where son los mismos: where ((a or b) or c) es equivalente a where a or b or c .

Una subconsulta puede a menudo (si no siempre) fusionarse con la consulta principal para simplificarla. En mi experiencia, esto a menudo mejora considerablemente el rendimiento:

select foo.a, bar.a from foomatic foo, bartastic bar where foo.id = bar.id and bar.id = ( select ban.id from bantabulous ban where ban.bandana = 42 ) ;

es equivalente a

select foo.a, bar.a from foomatic foo, bartastic bar, bantabulous ban where foo.id = bar.id and bar.id = ban.id and ban.bandana = 42 ;

El uso de uniones ANSI separa una gran cantidad de lógica de "código mono" de las partes realmente interesantes de la cláusula where: la consulta anterior es equivalente a

select foo.a, bar.a from foomatic foo join bartastic bar on bar.id = foo.id join bantabulous ban on ban.id = bar.id where ban.bandana = 42 ;

Si desea verificar la existencia de una fila, no use count (*) , en su lugar utilice rownum = 1 o coloque la consulta en una cláusula where exists para buscar solo una fila en lugar de todas.


Aunque la simplificación puede no ser igual a la optimización, la simplificación puede ser importante al escribir un código SQL legible, lo que a su vez es crítico para poder verificar la corrección conceptual de su código SQL (no la corrección sintáctica, que su entorno de desarrollo debería verificar). Me parece que en un mundo ideal, escribiríamos el código SQL más simple y legible, y luego el optimizador reescribirá ese código SQL para que esté en la forma que sea (quizás más detallada) funcionaría más rápido.

He descubierto que pensar en sentencias de SQL basadas en lógica de conjuntos es muy útil, especialmente si necesito combinar cláusulas where o descubrir una negación compleja de una cláusula where. Yo uso las leyes del álgebra booleana en este caso.

Los más importantes para simplificar una cláusula Where son probablemente las Leyes de DeMorgan (tenga en cuenta que "·" es "AND" y "+" es "O"):

  • NO (x · y) = NO x + NO y
  • NO (x + y) = NO x · NO y

Esto se traduce en SQL para:

NOT (expr1 AND expr2) -> NOT expr1 OR NOT expr2 NOT (expr1 OR expr2) -> NOT expr1 AND NOT expr2

Estas leyes pueden ser muy útiles para simplificar las cláusulas where con muchas partes AND y OR anidadas.

También es útil recordar que la declaración field1 IN (value1, value2, ...) es equivalente a field1 = value1 OR field1 = value2 OR ... Esto le permite negar el IN () una de dos maneras:

NOT field1 IN (value1, value2) -- for longer lists NOT field1 = value1 AND NOT field1 = value2 -- for shorter lists

También se puede pensar en una subconsulta de esta manera. Por ejemplo, esta cláusula negated where:

NOT (table1.field1 = value1 AND EXISTS (SELECT * FROM table2 WHERE table1.field1 = table2.field2))

puede ser reescrito como:

NOT table1.field1 = value1 OR NOT EXISTS (SELECT * FROM table2 WHERE table1.field1 = table2.field2))

Estas leyes no le dicen cómo transformar una consulta SQL usando una subconsulta en una utilizando una combinación, pero la lógica booleana puede ayudarlo a comprender los tipos de combinación y lo que debería devolver su consulta. Por ejemplo, con las tablas A y B , una INNER JOIN es como A AND B , una LEFT OUTER JOIN es como (A AND NOT B) OR (A AND B) que simplifica a A OR (A AND B) , y un FULL OUTER JOIN es A OR (A AND B) OR B que se simplifica a A OR B


Dada la naturaleza de SQL, debe ser consciente de las implicaciones de rendimiento de cualquier refactorización. La refactorización de las aplicaciones SQL es un buen recurso en la refactorización con un gran énfasis en el rendimiento (consulte el Capítulo 5).


Me gusta que todos en un equipo sigan un conjunto de estándares para que el código sea legible, fácil de mantener, comprensible, lavable, etc. :)

  • todos usan el mismo alias
  • sin cursores no hay bucles
  • ¿Por qué pensar en IN cuando puedes EXISTE?
  • SANGRAR
  • Consistencia en el estilo de codificación

hay más cosas aquí ¿Cuáles son algunos de sus estándares de base de datos más útiles?


Me gusta reemplazar todo tipo de subselección por consulta de unión.

Este es obvio:

SELECT * FROM mytable mo WHERE EXISTS ( SELECT * FROM othertable o WHERE o.othercol = mo.col )

por

SELECT mo.* FROM mytable mo inner join othertable o on o.othercol = mo.col

Y este está bajo estimación:

SELECT * FROM mytable mo WHERE NOT EXISTS ( SELECT * FROM othertable o WHERE o.othercol = mo.col )

por

SELECT mo.* FROM mytable mo left outer join othertable o on o.othercol = mo.col WHERE o.othercol is null

Podría ayudar al SGBD a elegir el buen plan de ejecución en una gran solicitud.


Mi enfoque es aprender la teoría relacional en general y el álgebra relacional en particular. A continuación, aprenda a detectar los constructos utilizados en SQL para implementar operadores del álgebra relacional (por ejemplo, cuantificación universal, también conocida como división) y cálculo (por ejemplo, cuantificación existencial). El problema es que SQL tiene características que no se encuentran en el modelo relacional, por ejemplo, valores nulos, que probablemente sean mejor refactorizados de todos modos. Lectura recomendada: SQL y teoría relacional: cómo escribir código SQL preciso por fecha CJ .

En este sentido, no estoy convencido de que "el hecho de que la mayoría de los SUBSELECTOS puedan reescribirse como un JOIN" represente una simplificación.

Tome esta consulta por ejemplo:

SELECT c FROM T1 WHERE c NOT IN ( SELECT c FROM T2 );

Reescribir usando JOIN

SELECT DISTINCT T1.c FROM T1 NATURAL LEFT OUTER JOIN T2 WHERE T2.c IS NULL;

¡La unión es más detallada!

Alternativamente, reconozca que el constructo está implementando un anti-enlace en la proyección de p ej. Pseudo algrbra

T1 { c } antijoin T2 { c }

Simplificación usando operadores relacionales:

SELECT c FROM T1 EXCEPT SELECT c FROM T2;


  • Supongo que lo más obvio es buscar cualquier cursor que pueda reemplazarse con una operación basada en SQL ''Set''.
  • Siguiente en mi lista, es buscar cualquier sub-consultas correlacionadas que se pueden volver a escribir como una consulta no correlacionada
  • En procedimientos almacenados largos, divida instrucciones SQL separadas en sus propios procedimientos almacenados. De esa forma, obtendrán su propio plan de consulta en caché.
  • Busque transacciones que puedan acortar su alcance. Regularmente encuentro declaraciones dentro de una transacción que pueden estar fuera de manera segura.
  • Los sub-selectores a menudo se pueden reescribir como combinaciones directas (los optimizadores modernos son buenos para detectar los simples)

Como mencionó @Quassnoi, el Optimizador a menudo hace un buen trabajo. Una forma de ayudarlo es garantizar que los índices y las estadísticas estén actualizados y que existan índices adecuados para la carga de trabajo de su consulta.