Ci stavo pensando alcuni giorni fa dopo un'ottimizzazione SQL. Penso che possiamo concordare sul fatto che SQL è un "linguaggio dichiarativo" nella definizione di Wikipedia:
Paradigma di programmazione che esprime la logica del calcolo senza descriverne il flusso di controllo
Se pensi a quante cose vengono fatte dietro le tende (guardando le statistiche, decidendo se un indice è utile, andando per un nidificato, unito o hash join, ecc. Ecc.) Dobbiamo ammettere che diamo solo un alto livello logica e il database si è occupato di tutta la logica del flusso di controllo di basso livello.
Anche in questo scenario, a volte l'ottimizzatore del database necessita di alcuni "suggerimenti" da parte dell'utente per ottenere i migliori risultati.
Un'altra definizione comune di linguaggio "dichiarativo" è (non riesco a trovare una fonte autorevole):
Paradigma di programmazione che esprime il risultato desiderato del calcolo senza descrivere i passaggi per raggiungerlo (anche abbreviato con "descrivi cosa, non come")
Se accettiamo questa definizione, incontriamo i problemi descritti dal PO.
Il primo problema è che SQL ci offre molteplici modi equivalenti per definire "lo stesso risultato". Probabilmente è un male necessario: più potere espressivo diamo a una lingua, più è probabile che abbia modi diversi di esprimere la stessa cosa.
Ad esempio, una volta mi è stato chiesto di ottimizzare questa query:
SELECT Distinct CT.cust_type, ct.cust_type_description
from customer c
INNER JOIN
Customer_type CT on c.cust_type=ct.cust_type;
Dato che i tipi erano molto meno del cliente e c'era un indice sulla cust_type
tabella dei clienti, ho ottenuto un grande miglioramento riscrivendolo come:
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);
In questo caso specifico, quando ho chiesto allo sviluppatore cosa voleva ottenere, mi ha detto "Volevo tutti i tipi di clienti per i quali avevo almeno un cliente", per inciso è esattamente come si potrebbe descrivere la query dell'ottimizzatore.
Quindi, se potessi trovare una query equivalente e più efficiente, perché l'ottimizzatore non può fare lo stesso?
La mia ipotesi migliore è che sia per due motivi principali:
SQL esprime la logica:
poiché SQL esprime una logica di alto livello, vorremmo davvero che l'ottimizzatore "superasse" noi e la nostra logica? Griderei con entusiasmo "sì" se non fosse per tutte le volte in cui ho dovuto forzare l'ottimizzatore a scegliere il percorso di esecuzione più efficiente. Penso che l'idea potrebbe essere quella di consentire all'ottimizzatore di fare del suo meglio (anche rivedendo la nostra logica) ma di darci un "meccanismo di suggerimento" per venire in soccorso quando qualcosa impazzisce (sarebbe come avere la ruota + i freni dentro un'auto autonoma).
Più scelte = più tempo
Anche il miglior ottimizzatore RDBMS non verifica TUTTI i possibili percorsi di esecuzione, in quanto devono essere molto veloci: quanto sarebbe bello ottimizzare una query da 100ms a 10ms se dovessi impiegare ogni volta 100ms a scegliere il percorso migliore? E questo è con l'ottimizzatore che rispetta la nostra "logica di alto livello". Se dovesse testare anche tutte le query SQL equivalenti, il tempo di ottimizzazione potrebbe aumentare più volte.
Un altro buon esempio di riscrittura di query che nessun RDBMS è effettivamente in grado di fare è (da questo interessante post sul 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;
di quanto può essere scritto come questo (funzioni analitiche richieste)
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)
. Dovrebbe essere banale vedere come riaffermarlo con unexists
o unjoin
.