SQL è la lingua franca per l'analisi dei dati strutturati da diversi decenni, e negli ultimi anni abbiamo lavorato molto per supportare ANSI SQL e le varie estensioni per rendere SQL più piacevole da usare su Databricks. Oggi, siamo entusiasti di annunciare la sintassi pipe SQL, la più grande estensione che abbiamo fatto negli ultimi anni per rendere SQL drasticamente più facile da scrivere e capire, in modo completamente retrocompatibile.
Una delle sfide chiave in SQL stesso finora risiede nell'ordinamento della "logica". Quando si scrive una query, molti autori pensano in termini dei seguenti passaggi logici: identificare l'elenco delle tabelle da interrogare, unirle, filtrare le righe indesiderate e, infine, aggregare. Questo ordinamento logico può essere espresso nel modo seguente:
La query SQL per questi passaggi sarebbe la seguente:
Invece di scrivere i passaggi in ordine (1, 2, 3) dobbiamo invece scriverli nell'ordine (3, 2, 1). Questo è confusionario e il problema si aggrava solo quando aggiungiamo più logica e passaggi a ciascuna query.
Al contrario, pensiamo ai DataFrames. Una grande fonte della popolarità originale di Apache Spark tra i data scientist è la potente capacità delle sue API DataFrame Scala e Python. I programmi possono applicarle per esprimere la loro logica in un ordinamento naturale dei passaggi. Partendo dalla tabella sorgente, gli utenti possono concatenare operazioni indipendenti e componibili una dopo l'altra, rendendo più facile costruire trasformazioni di dati complesse in una sequenza chiara e intuitiva.
Questo design promuove la leggibilità e semplifica il debug mantenendo la flessibilità. È una delle ragioni principali per cui Databricks ha ottenuto la sua massiccia crescita nel settore finora nello spazio della gestione dei dati, e questo slancio continua solo ad aumentare oggi.
Ecco come appare la stessa logica nei DataFrames PySpark:
Questo approccio supporta un'iterazione flessibile sulle idee. Sappiamo che i dati sorgente esistono in qualche file, quindi possiamo iniziare subito creando un DataFrame che rappresenta quei dati come una relazione. Dopo aver pensato un po', ci rendiamo conto che vogliamo filtrare le righe in base alla colonna stringa. OK, quindi possiamo aggiungere un passaggio .filter alla fine del DataFrame precedente. Oh, e vogliamo calcolare una proiezione alla fine, quindi la aggiungiamo alla fine della sequenza.
Molti di questi utenti desiderano che SQL si comporti in modo più simile ai linguaggi di dati moderni come questo. Storicamente, ciò non era possibile e gli utenti dovevano scegliere un modo di pensare o l'altro.
Avanti veloce a oggi: ora è possibile avere il meglio di entrambi i mondi! La sintassi pipe rende SQL sia più facile da scrivere che da leggere ed estendere in seguito, e ci libera da questa confusione permettendoci di usare semplicemente gli stessi passaggi nell'ordine in cui li abbiamo pensati.
Alla conferenza VLDB 2024, Google ha pubblicato un paper industriale che propone questo come nuovo standard. Gli ingegneri di elaborazione delle query hanno implementato questa funzionalità e l'hanno abilitata per impostazione predefinita in Apache Spark 4.0 (documentazione) e Databricks Runtime 16.2 (documentazione) e versioni successive. È retrocompatibile con la sintassi SQL normale: gli utenti possono scrivere intere query utilizzando questa sintassi, solo determinate sottoquery o qualsiasi combinazione utile.
Il paper industriale fornisce la query 13 dal benchmark TPC-H come primo esempio:
Utilizzando la sintassi pipe per esprimere la stessa logica, applichiamo gli operatori in sequenza dall'inizio alla fine con qualsiasi ordinamento arbitrario:
Con SQL normale, quando vogliamo raccogliere righe in gruppi in base ai valori di colonne o espressioni, aggiungiamo una clausola GROUP BY alla fine della query SQL in costruzione. Le aggregazioni da eseguire rimangono bloccate in cima alla lista SELECT all'inizio della query e ogni espressione deve ora essere:
Qualsiasi elemento SELECT che non soddisfa una di queste categorie genererà un errore come "espressione X è apparsa nella lista SELECT ma non è stata raggruppata o aggregata".
Anche le regole della clausola WHERE cambiano:
La sintassi pipe risolve questo problema separando ogni operazione di aggregazione (con possibile raggruppamento) in un passaggio dedicato che può essere applicato in qualsiasi momento. Solo le espressioni con funzioni di aggregazione all'interno possono apparire in questo passaggio, e le funzioni di aggregazione non possono apparire nei passaggi |> SELECT. Se l'autore SQL dimentica uno qualsiasi di questi invarianti, i messaggi di errore risultanti sono molto chiari e facili da capire.
Inoltre, non è più necessario ripetere le espressioni di raggruppamento, poiché possiamo scriverle in un'unica clausola GROUP BY.
Guardiamo l'esempio precedente con un'aggregazione aggiunta alla fine, che restituisce una tabella di risultati con due colonne L, M:
SQL normale generalmente richiede che le clausole appaiano in un ordine specifico, senza ripetizioni. Se vogliamo applicare ulteriori operazioni al risultato di una query SQL, il modo per farlo è utilizzare una sottoquery tabellare in cui racchiudiamo la query originale tra parentesi e la utilizziamo nella clausola FROM di una query racchiusa. La query all'inizio di questo post mostra un semplice esempio di ciò.
Si noti che questa nidificazione può avvenire un numero arbitrario di volte. Ad esempio, ecco la query 23 di TPC-DS:
Sta diventando confusionario e difficile da leggere con tutti i livelli di parentesi e indentazione!
Dall'altra parte, con la sintassi pipe di SQL non c'è bisogno di subquery di tabelle. Poiché gli operatori pipe possono apparire in qualsiasi ordine, possiamo semplicemente aggiungerne di nuovi alla fine in qualsiasi momento, e tutti i passaggi funzionano ancora allo stesso modo.
La sintassi pipe rielabora il modo in cui gli autori scrivono, leggono ed estendono SQL. Potrebbe sembrare una sfida passare dal modo di pensare di SQL regolare a questo nuovo paradigma. Potresti persino avere un ampio corpo di query SQL esistenti scritte in precedenza di cui sei responsabile della manutenzione e della possibile estensione futura. Come possiamo far funzionare questo con due sintassi SQL?
Fortunatamente, questo non è un problema con la nostra nuova sintassi SQL. È completamente interoperabile con SQL regolare, dove qualsiasi query (o subquery di tabella) può apparire utilizzando una delle due sintassi. Possiamo iniziare a scrivere nuove query utilizzando la sintassi pipe di SQL e mantenere le nostre precedenti se necessario. Possiamo persino iniziare a sostituire le subquery di tabelle delle nostre query precedenti con la nuova sintassi e mantenere tutto il resto uguale, come aggiornare solo una parte di TPC-H Q13 dall'inizio di questo post:
Poiché gli operatori pipe di SQL possono seguire qualsiasi query valida, è anche possibile iniziare ad aggiungerli a query SQL regolari esistenti. Ad esempio:
La sintassi pipe di SQL è pronta per essere provata in Databricks Runtime versione 16.2 e successive. Oppure scarica Apache Spark 4.0 e provala nel mondo open source. La sintassi è conforme al paper industriale Pipe Syntax in SQL, quindi la nuova sintassi sarà portabile con Google BigQuery così come con il progetto open source ZetaSQL.
Questa sintassi sta anche iniziando a generare interesse nella community e ad apparire altrove, aumentando la portabilità ora e nel tempo.
Provala e sperimenta i vantaggi di rendere le query SQL più semplici da scrivere per utenti nuovi ed esperti, e rendi la leggibilità e l'estensibilità future più facili riducendo l'incidenza di subquery complesse a favore di operatori chiari e componibili.
(Questo post sul blog è stato tradotto utilizzando strumenti basati sull'intelligenza artificiale) Post originale
