Estaba pensando en esto hace unos días después de una optimización de SQL. Creo que podemos estar de acuerdo en que SQL es un "lenguaje declarativo" en la definición de Wikipedia:
Paradigma de programación que expresa la lógica de la computación sin describir su flujo de control.
Si piensa cuántas cosas se hacen detrás de las cortinas (mirar estadísticas, decidir si un índice es útil, buscar una unión anidada, combinada o hash, etc.), debemos admitir que solo damos un alto nivel lógica, y la base de datos se encargó de toda la lógica de flujo de control de bajo nivel.
También en este escenario, a veces el optimizador de la base de datos necesita algunas "sugerencias" del usuario para dar los mejores resultados.
Otra definición común de lenguaje "declarativo" es (no puedo encontrar una fuente autorizada):
Paradigma de programación que expresa el resultado deseado de la computación sin describir los pasos para lograrlo (también abreviado con "describir qué, no cómo")
Si aceptamos esta definición, nos encontramos con los problemas descritos por el OP.
El primer problema es que SQL nos brinda múltiples formas equivalentes de definir "el mismo resultado". Probablemente sea un mal necesario: cuanto más poder expresivo le otorguemos a un idioma, es más probable que tenga diferentes formas de expresar lo mismo.
Como ejemplo, una vez me han pedido que optimice esta consulta:
SELECT Distinct CT.cust_type, ct.cust_type_description
from customer c
INNER JOIN
Customer_type CT on c.cust_type=ct.cust_type;
Como los tipos eran mucho menos que el cliente y había un índice en la cust_type
tabla de clientes, he logrado una gran mejora al reescribirlo como:
SELECT CT.cust_type, ct.cust_type_description
from Customer_type CT
Where exists ( select 1 from customer c
Where c.cust_type=ct.cust_type);
En este caso específico, cuando le pregunté al desarrollador qué quería lograr, él me dijo "Quería todos los tipos de clientes para los que tenía al menos un cliente", que por cierto es exactamente cómo se podría describir la consulta del optimizador.
Entonces, si pudiera encontrar una consulta equivalente y más eficiente, ¿por qué el optimizador no puede hacer lo mismo?
Mi mejor conjetura es que es por dos razones principales:
SQL expresa lógica:
dado que SQL expresa una lógica de alto nivel, ¿realmente queremos que el optimizador nos "engañe" a nosotros y a nuestra lógica? Gritaría con entusiasmo "sí" si no fuera por todas las veces que tuve que forzar al optimizador a elegir la ruta de ejecución más eficiente. Creo que la idea podría ser permitir que el optimizador haga su mejor esfuerzo (también revisando nuestra lógica) pero dándonos un "mecanismo de pista" para que salga al rescate cuando algo se vuelva loco (sería como tener la rueda + frenos en Un coche autónomo).
Más opciones = más tiempo
Incluso el mejor optimizador RDBMS no prueba TODAS las rutas de ejecución posibles, ya que deben ser realmente rápidas: ¿qué tan bueno sería optimizar una consulta de 100ms a 10ms si necesito pasar cada 100ms eligiendo la mejor ruta? Y eso es con el optimizador respetando nuestra "lógica de alto nivel". Si también probara todas las consultas SQL equivalentes, el tiempo del optimizador podría crecer varias veces.
Otro buen ejemplo de reescritura de consultas que no es capaz de hacer RDBMS es (de esta interesante publicación de blog )
SELECT t1.id, t1.value, SUM(t2.value)
FROM mytable t1
JOIN mytable t2
ON t2.id <= t1.id
GROUP BY t1.id, t1.value;
de lo que se puede escribir así (se requieren funciones analíticas)
SELECT id, value, SUM(t1.value) OVER (ORDER BY id)
FROM mytable
select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param)
. Debería ser trivial ver cómo reafirmar eso con unexists
o ajoin
.