SQL è dichiarativo?


22

Chiedo perché molte delle domande che vedo in SQL equivalgono a: "È lento. Come posso accelerarlo"? O sono tutorial che affermano "Fallo in questo modo e non in quel modo perché è più veloce".

Mi sembra che gran parte di SQL sappia esattamente come verrebbe eseguita un'espressione e da quella conoscenza che sceglie stili di espressione che funzionano meglio. Ciò non coincide con un aspetto della programmazione dichiarativa: quello di lasciare il sistema per decidere come eseguire al meglio il calcolo specificando semplicemente quale dovrebbe essere il calcolo.

Un motore SQL non dovrebbe preoccuparsi se lo hai usato in, existso joinse è veramente dichiarativo, non dovrebbe darti la risposta corretta in tempo ragionevole, se possibile, con uno dei tre metodi? Quest'ultimo esempio è suggerito da questo post recente che è del tipo menzionato nel mio paragrafo di apertura.

indici

Immagino che l'esempio più semplice che avrei potuto usare riguardi la creazione di un indice per una tabella. Il gumph qui su w3schools.com cerca persino di spiegarlo come qualcosa di non visto dall'utente che è lì per motivi di prestazioni. La loro descrizione sembra mettere gli indici SQL nel campo non dichiarativo e vengono regolarmente aggiunti a mano per motivi puramente prestazionali.

È il caso che il loro sia da qualche parte un DB SQL ideale che è molto più dichiarativo di tutto il resto, ma perché è così buono che non ne sentiamo parlare?


@FrustratedWithFormsDesigner: so esattamente cosa significa. select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param). Dovrebbe essere banale vedere come riaffermarlo con un existso un join.
Mason Wheeler,

Usando un ragionamento simile suppongo che le espressioni regolari siano un metodo di espressione più dichiarativo poiché raramente vedo le domande sulle prestazioni con risposta "dovresti scriverle in questo modo per ottenere prestazioni migliori". Mi sto distruggendo il cervello e riesco a ricordare per metà qualche domanda da fare con affermazioni negative o anticipate in una lente regexp in cui la risposta era di riscrivere la regexp in un modo diverso di fare lo stesso in meno tempo.
Paddy3118,

Le prestazioni sono un dettaglio di implementazione. Le prestazioni di quasi tutte le implementazioni IN potrebbero essere comparabili o migliori di EXISTS e JOIN se gli sviluppatori di Query Processor ritenessero che fosse una priorità.
Giustino il

1
@JustinC, sembra essere più che un dettaglio data la preponderanza di domande e suggerimenti SQL orientati alle prestazioni per un linguaggio apparentemente dichiarativo?
Paddy3118

Non esiste una definizione chiara di un linguaggio di programmazione dichiarativo, quindi non ha senso parlarne. Alcune lingue sono di livello superiore rispetto ad altre, tutto qui.
gardenhead

Risposte:


21

SQL è teoricamente dichiarativo. Ma sai cosa dicono della differenza tra teoria e pratica ...

Alla base, il concetto di "programmazione dichiarativa" non è mai stato veramente efficace, e probabilmente non lo sarà mai fino a quando non avremo un compilatore basato sull'intelligenza artificiale in grado di guardare il codice e rispondere alla domanda "Qual è l'intenzione di questo codice?" in modo intelligente, come farebbe la persona che l'ha scritta. Al centro di ogni linguaggio dichiarativo c'è un sacco di codice imperativo che cerca freneticamente di risolvere quel problema senza l'aiuto di un'intelligenza artificiale.

Spesso funziona sorprendentemente bene, perché i casi più comuni sono casi comuni , che le persone che hanno scritto l'implementazione del linguaggio conoscevano e hanno trovato buoni modi per gestire. Ma poi ti imbatti in un caso limite che l'implementatore non ha preso in considerazione e vedi che le prestazioni peggiorano rapidamente poiché l'interprete è costretto a prendere il codice molto più letteralmente e gestirlo in modo meno efficiente.


3
Mai veramente efficace? SQL, LINQ, Knockout.js, Prolog, linguaggio ELM. Potresti voler ricontrollare. Attualmente sto utilizzando principalmente tecnologie dichiarative.
Brian

5
@brian: E tutti loro degenerano piuttosto rapidamente quando ti imbatti in un caso limite che nessuno ha pensato. Suppongo che avrei dovuto dire "mai veramente efficace nel caso generale ".
Mason Wheeler,

Quando la tua risposta è impostata per degradare visto che è archiviata in un database di SQL Server? :) Raramente ho colpito un caso limite in nessuno di essi che non poteva essere risolto all'interno del framework. Vedo da dove vieni, ma i casi limite non mi causano molto dolore per quanto sia vantaggioso e facile ragionare sul 99% del codice dichiarativo. È come dire Clojure o F # è un male perché hai dovuto usare un tipo mutevole per risolvere il tuo problema.
Brian

11
@brian: I rarely hit an edge case in any of them that couldn't be solved within the framework.Sì, questo è il punto: dover trovare un modo per risolverli all'interno del framework perché il framework non è abbastanza intelligente da risolverlo per te nel modo in cui lo hai dichiarato inizialmente.
Mason Wheeler,

Che dire di selezionare ... per l'aggiornamento? Sembra un comando imperativo.
Jesvin Jose,

6

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_typetabella 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

1
L'esempio di riscrivere il join in un esiste è interessante. Una regola empirica che cerco di impressionare sugli sviluppatori SQL è che l'uso di DISTINCT è un odore di codice: o la query, o il modello di dati, è molto probabilmente errato e dovrebbe essere cercato un approccio diverso.
David Aldridge,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.