¿SQL es declarativo?

22

Pregunto porque muchas de las preguntas que veo en SQL equivalen a: "Esto es lento. ¿Cómo puedo acelerarlo?" ¿O hay tutoriales que dicen "Haz esto de esta manera y no de esa manera ya que es más rápido".

Me parece que una gran parte de SQL es saber cómo se realizaría una expresión y, a partir de ese conocimiento, elegir estilos de expresión que funcionen mejor. Esto no cuadra con un aspecto de la programación declarativa: el de dejar el sistema para decidir la mejor manera de realizar el cálculo con usted, solo especificando qué debe producir el cálculo.

¿No debería importarle un motor SQL si lo usó in, existso joinsi es verdaderamente declarativo, no debería simplemente darle la respuesta correcta en un tiempo razonable si es posible por cualquiera de los tres métodos? Este último ejemplo es impulsado por esta publicación reciente que es del tipo mencionado en mi párrafo inicial.

Índices

Supongo que el ejemplo más fácil que podría haber usado se relaciona con la creación de un índice para una tabla. El error aquí en w3schools.com incluso trata de explicarlo como algo invisible para el usuario que está allí por razones de rendimiento. Su descripción parece colocar índices SQL en el campo no declarativo y se agregan a mano de forma rutinaria por razones puramente de rendimiento.

¿Es el caso de que su lugar es un DB SQL ideal que es mucho más declarativo que el resto, pero porque es bueno que uno no se entere?

Paddy3118
fuente
@FrustratedWithFormsDesigner: Sé exactamente lo que eso significa. 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 un existso a join.
Mason Wheeler
Utilizando un razonamiento similar, supongo que las expresiones regulares son un método de expresión más declarativo, ya que rara vez veo preguntas de rendimiento respondidas por "deberías escribirlo de esta manera para obtener un mejor rendimiento". Me estoy destrozando el cerebro y puedo recordar a medias alguna pregunta relacionada con afirmaciones negativas de mirar hacia atrás o hacia adelante en una expresión regular lenta donde la respuesta fue reescribir la expresión regular de una manera diferente para hacer lo mismo en menos tiempo.
Paddy3118
El rendimiento es un detalle de implementación. El rendimiento de casi cualquier implementación de IN podría ser comparable o mejor que EXISTE y UNIRSE si los desarrolladores del procesador de consultas lo consideraran una prioridad.
JustinC
1
@JustinC, parece ser más que un detalle dada la preponderancia de preguntas y consejos SQL orientados al rendimiento para un lenguaje supuestamente declarativo.
Paddy3118
No existe una definición clara de un lenguaje de programación declarativo, por lo que no tiene sentido hablar de eso. Algunos idiomas son de mayor nivel que otros, eso es todo.
cabeza de jardín

Respuestas:

21

SQL es teóricamente declarativo. Pero sabes lo que dicen sobre la diferencia entre teoría y práctica ...

En esencia, el concepto de "programación declarativa" nunca ha sido realmente efectivo, y probablemente nunca lo será hasta que tengamos un compilador basado en IA que sea capaz de mirar el código y responder a la pregunta "¿cuál es la intención de este código?" inteligentemente, de la misma manera que lo haría la persona que lo escribió. En el corazón de cada lenguaje declarativo hay un montón de código imperativo que intenta frenéticamente resolver ese problema sin la ayuda de una IA.

A menudo funciona sorprendentemente bien, porque los casos más comunes son casos comunes , que las personas que escribieron la implementación del lenguaje conocían y encontraron buenas maneras de manejarlo. Pero luego se encuentra con un caso límite que el implementador no consideró, y ve que el rendimiento se degrada rápidamente ya que el intérprete se ve obligado a tomar el código mucho más literalmente y manejarlo de una manera menos eficiente.

Mason Wheeler
fuente
3
¿Nunca es realmente efectivo? SQL, LINQ, Knockout.js, Prolog, lenguaje ELM. Es posible que desee verificar de nuevo. Estoy usando principalmente tecnologías declarativas en este momento.
Brian
55
@brian: Y todos degeneran bastante rápido cuando te topas con un caso límite en el que nadie pensó. Supongo que debería haber dicho "nunca realmente efectivo en el caso general ".
Mason Wheeler
¿Cuándo se configura su respuesta para degradarse, ya que se almacena en una base de datos de SQL Server? :) Raramente llego a un caso límite en ninguno de ellos que no pueda resolverse dentro del marco. Veo de dónde vienes, pero los casos extremos realmente no me causan mucho dolor por lo beneficioso y fácil de razonar que es el 99% del código declarativo. Es como decir que Clojure o F # es malo porque tenía que usar un tipo mutable para resolver su problema.
Brian
11
@brian: I rarely hit an edge case in any of them that couldn't be solved within the framework.Sí, ese es el punto: tener que encontrar una manera de resolverlos dentro del marco porque el marco no es lo suficientemente inteligente como para resolverlo de la manera en que lo declaró originalmente.
Mason Wheeler
¿Qué pasa con seleccionar ... para actualizar? Parece un comando imperativo.
Jesvin Jose
6

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_typetabla 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
Insac
fuente
1
El ejemplo de reescribir la unión a un existe es interesante. Una regla general que trato de impresionar a los desarrolladores de SQL es que el uso de DISTINCT es un olor a código: es muy posible que la consulta o el modelo de datos sean incorrectos, y se debe buscar un enfoque diferente.
David Aldridge, el