
Nella precedente Ho descritto il concetto e l'implementazione di un database basato su funzionalità, piuttosto che su tabelle e campi come nei database relazionali. Sono stati forniti molti esempi che mostrano i vantaggi di questo approccio rispetto al classico. Molti li hanno considerati poco convincenti.
In questo articolo, mostrerò come tale concetto consenta di bilanciare rapidamente e facilmente le operazioni di scrittura e lettura nel database senza alcuna modifica alla logica di funzionamento. Funzionalità simili sono state tentate nelle moderne DBMS commerciali (in particolare, Oracle e Microsoft SQL Server). Alla fine dell'articolo, mostrerò che i risultati ottenuti non sono stati, per usare un eufemismo, eccellenti.
Descrizione
Come in precedenza, per una migliore comprensione inizierò con degli esempi. Supponiamo che dobbiamo implementare la logica che restituisce un elenco dei reparti, insieme al numero di dipendenti in essi e al loro stipendio totale.
In un database funzionale, questo apparirebbe come segue:
CLASS Department ‘Dipartimento’;
name ‘Nome’ = DATA STRING[100] (Dipartimento);
CLASS Employee ‘Dipendente’;
department ‘Reparto’ = DATA Department (Employee);
salary ‘Stipendio’ = DATA NUMERIC[10,2] (Employee);
countEmployees ‘Numero di dipendenti’ (Department d) =
SOMMA GRUPPO 1 SE dipartimento (Dipendente e) = d;
salarySum ‘Stipendio totale’ (Department d) =
GROUP SUM salary (Dipendente e) IF department(e) = d;
SELEZIONA name(Dipartimento d), countEmployees(d), salarySum(d);
La complessità dell'esecuzione di questa query in qualsiasi RDBMS sarà equivalente a O(numero di dipendenti), poiché per questo calcolo è necessario esaminare l'intera tabella dei dipendenti e poi raggrupparli per dipartimento. Ci sarà anche un piccolo (si consideri che i dipendenti sono di gran lunga superiori ai dipartimenti) supplemento a seconda del piano scelto. O(log numero di dipendenti) o O(numero di dipartimenti) per il raggruppamento e altro.
È chiaro che i costi di esecuzione possono variare nei vari RDBMS, ma la complessità non cambierà.
Nell'implementazione proposta, il RDBMS funzionale formerà una sottoquery che calcolerà i valori necessari per dipartimento e poi eseguirà un JOIN con la tabella dei dipartimenti per ottenere il nome. Tuttavia, per ogni funzione durante la dichiarazione è possibile specificare un marcatore speciale MATERIALIZED. Il sistema creerà automaticamente il campo corrispondente per ogni funzione di questo tipo. Quando il valore della funzione cambia, anche il valore del campo verrà modificato nella stessa transazione. Quando si accede a questa funzione, si accede già al campo precalcolato.
In particolare, se si imposta MATERIALIZED per le funzioni countEmployees e salarySum, nella tabella con l'elenco dei reparti verranno aggiunti due campi, in cui saranno memorizzati il numero di dipendenti e il loro stipendio totale. In caso di modifiche ai dipendenti, ai loro stipendi o alla loro appartenenza ai reparti, il sistema aggiornerà automaticamente i valori di questi campi. La query sopra riportata si riferirà direttamente a questi campi e verrà eseguita in O(numero di dipartimenti).
Quali sono le limitazioni? Solo una: una funzione deve avere un numero finito di valori di input per cui il suo valore è definito. Altrimenti, non sarà possibile costruire una tabella che memorizzi tutti i suoi valori, poiché non può esistere una tabella con un numero infinito di righe.
Esempio:
employeesCount ‘Numero di dipendenti con stipendio > N’ (Dipartimento d, NUMERIC[10,2] N) =
SOMMA GRUPPO stipendio(Dipendente e) SE dipartimento(e) = d E stipendio(e) > N;
Questa funzione è definita per un numero infinito di valori del numero N (ad esempio, qualsiasi valore negativo va bene). Pertanto, non è possibile impostare MATERIALIZED su di essa. Così facendo, si tratta di una limitazione logica, non tecnica (cioè non perché non siamo stati in grado di implementarla). Altrimenti, nessuna limitazione. È possibile utilizzare raggruppamenti, ordinamenti, AND e OR, PARTITION, ricorsioni, ecc.
Ad esempio, nel compito 2.2 dell'articolo precedente, si può impostare MATERIALIZED su entrambe le funzioni:
acquistato 'Acquistato' (Cliente c, Prodotto p, INTERO y) =
SOMMA GRUPPO somma(Dettaglio d) SE
cliente(forzaOrdine(d)) = c E
prodotto(d) = p E
estraiAnno(data(ordina(d))) = y MATERIALIZZATO;
valutazione 'Valutazione' (Cliente c, Prodotto p, INTERO y) =
PARTIZIONE SOMMA 1 ORDINE DESC acquistato(c, p, y), p PER c, y MATERIALIZZATO;
SELEZIONA nomeContatto(Cliente c), nome(Prodotto p) DOVE valutazione(c, p, 1997) < 3;
Il sistema creerà automaticamente una tabella con le chiavi dei tipi Cliente, Prodotto e INTERO, aggiungerà due campi e aggiornerà i valori di questi campi in caso di qualsiasi modifica. Nei successivi accessi a queste funzioni non ci sarà il calcolo, ma verranno letti i valori dei campi corrispondenti.
Attraverso questo meccanismo si possono, ad esempio, eliminare le ricorsioni (CTE) nelle query. In particolare, consideriamo i gruppi che formano un albero tramite la relazione child/parent (ogni gruppo ha un riferimento al proprio genitore):
genitore = DATA Group (Gruppo);
Nella base di dati funzionale, la logica delle ricorsioni può essere definita come segue:
livello (Gruppo figlio, Gruppo genitore) = RECURSIONE 1l SE il figlio È Gruppo E il genitore == figlio
PASSO 2l SE il genitore == genitore($parent);
èGenitore (Gruppo figlio, Gruppo genitore) = VERO SE livello(figlio, genitore) MATERIALIZZATO;
Poiché per la funzione isParent è stato impostato MATERIALIZED, verrà creata una tabella con due chiavi (gruppi), in cui il campo isParent sarà vero solo se la prima chiave è un discendente della seconda. Il numero di registrazioni in questa tabella sarà pari al numero di gruppi moltiplicato per la profondità media dell'albero. Se è necessario, ad esempio, contare il numero di discendenti di un determinato gruppo, si può accedere a questa funzione:
childrenCount (Gruppo g) = SOMMA GRUPPO 1 SE èGenitore(Gruppo child, g);
Non ci sarà alcun CTE nella query SQL. Invece, ci sarà un semplice GROUP BY.
Con questo meccanismo è anche facile denormalizzare il database se necessario:
CLASS Order 'Ordine';
data 'Data' = DATA DATE (Ordine);
CLASS OrderDetail 'Voce dell'ordine';
order 'Ordine' = DATA Order (OrderDetail);
date 'Data' (OrderDetail d) = date(order(d)) MATERIALIZED INDEXED;
Quando si richiede la funzione date per la voce dell'ordine, verrà eseguita una lettura dalla tabella delle righe d'ordine del campo sul quale è presente un indice. Quando la data dell'ordine cambia, il sistema ricalcolerà automaticamente la data denormalizzata nella riga.
Vantaggi
A cosa serve tutto questo meccanismo? Nelle classiche DBMS, senza riscrivere le query, sviluppatori o DBA possono solo modificare gli indici, definire le statistiche e consigliare al pianificatore di query come eseguirle (e i suggerimenti ci sono solo nei DBMS commerciali). Per quanto si sforzino, non riusciranno a eseguire la prima query nell'articolo in O (numero di dipartimenti) senza modificare le richieste e senza aggiungere trigger. Nello schema proposto, durante la fase di sviluppo non è necessario preoccuparsi della struttura di archiviazione dei dati e delle aggregazioni da utilizzare. Tutto questo può essere facilmente cambiato al volo direttamente in fase di produzione.
In pratica, questo è come si presenta. Alcune persone sviluppano direttamente la logica basata sulla richiesta. Non hanno competenze né negli algoritmi e nella loro complessità, né nei piani di esecuzione, né nei tipi di join, né in alcun altro aspetto tecnico. Queste persone sono più business analyst che sviluppatori. Dopodiché, tutto questo va in fase di test o in produzione. Viene attivato il logging delle query lunghe. Quando viene rilevata una query lunga, altre persone (più tecniche — essenzialmente DBA) decidono di attivare il MATERIALIZED su una certa funzione intermedia. Questo rallenta leggermente la scrittura (poiché è necessario aggiornare un campo aggiuntivo nella transazione). Tuttavia, non solo rende più veloce questa query, ma anche tutte le altre che utilizzano questa funzione. Inoltre, la decisione su quale funzione materializzare è relativamente semplice. I due parametri principali sono: il numero di valori di input possibili (tante saranno le righe nella tabella corrispondente) e la frequenza con cui viene utilizzata in altre funzioni.
Analoghi
Nei moderni DBMS commerciali ci sono meccanismi simili: MATERIALIZED VIEW con FAST REFRESH (Oracle) e INDEXED VIEW (Microsoft SQL Server). In PostgreSQL, la MATERIALIZED VIEW non può essere aggiornata in transazione, ma solo su richiesta (e con limitazioni molto rigorose), quindi non la consideriamo. Tuttavia, presentano diverse problematiche che limitano significativamente il loro utilizzo.
In primo luogo, è possibile attivare la materializzazione solo se è già stata creata una VIEW normale. Altrimenti, sarà necessario riscrivere le altre query per accedere alla nuova vista creata per utilizzare questa materializzazione. Oppure si può lasciare tutto com'è, ma sarà almeno inefficace se ci sono già alcuni dati pre-calcolati che molte query non utilizzano sempre, ma calcolano nuovamente.
In secondo luogo, presentano un'enorme quantità di limitazioni:
Oracle
5.3.8.4 Restrizioni generali sul Fast Refresh
La query definente della materialized view è limitata come segue:
- La materialized view non deve contenere riferimenti a espressioni non ripetitive come
SYSDATEandROWNUM.- La materialized view non deve contenere riferimenti a
RAWorLONGRAWtipi di dati.- Non può contenere una
SELECTsubquery di elenco.- Non può contenere funzioni analitiche (per esempio,
RANK) nellaSELECTclausola.- Non può fare riferimento a una tabella su cui è definito un
XMLIndexindice.- Non può contenere una
MODELclausola.- Non può contenere una
HAVINGclausola con una subquery.- Non può contenere query nidificate che abbiano
ANY,ALL, oNOTEXISTS.- Non può contenere una
[START WITH …] CONNECT BYclausola.- Non può contenere più tabelle di dettaglio in siti diversi.
ONCOMMITLe viste materializzate non possono avere tabelle di dettaglio remote.- Le viste materializzate annidate devono avere un join o una funzione aggregata.
- Le viste di join materializzate e le viste aggregate materializzate con un
GROUPBYnon possono selezionare da una tabella organizzata per indice.5.3.8.5 Restrizioni sul Fast Refresh delle Viste Materializzate con Solo Joins
Definire query per viste materializzate con solo joins e senza aggregati ha le seguenti restrizioni sul fast refresh:
- Tutte le restrizioni da ««.
- Non possono avere
GROUPBYclausole o aggregati.- I rowid di tutte le tabelle nella
DAlista devono apparire nellaSELECTlista della query.- I log delle viste materializzate devono esistere con rowid per tutte le tabelle di base nel
DAlista della query.- Non puoi creare una vista materializzata aggiornabile con refresh veloce da più tabelle con joins semplici che includono una colonna di tipo oggetto nella
SELECTdichiarazione.Inoltre, il metodo di refresh scelto non sarà ottimale se:
- La query di definizione utilizza un outer join che si comporta come un inner join. Se la query di definizione contiene un tale join, considera di riscrivere la query in modo che contenga un inner join.
- Il
SELECTla lista della vista materializzata contiene espressioni su colonne provenienti da più tabelle.5.3.8.6 Restrizioni sul Fast Refresh delle Viste Materializzate con Aggregati
Definire query per viste materializzate con aggregati o joins ha le seguenti restrizioni sul fast refresh:
- Tutte le restrizioni da ««.
Il fast refresh è supportato per entrambe le
ONCOMMITandONDEMANDviste materializzate, tuttavia si applicano le seguenti restrizioni:
- Tutte le tabelle nella vista materializzata devono avere log delle viste materializzate, e i log delle viste materializzate devono:
- Contenere tutte le colonne della tabella riferita nella vista materializzata.
- Specificare con
ROWIDandINCLUDINGNEWVALUES.- Specificare la
SEQUENCEclausola se ci si aspetta che la tabella abbia un mix di inserimenti/caricamenti diretti, cancellazioni e aggiornamenti.- Solo
SUM,CONTEGGIO,AVG,STDDEV,VARIANCE,MINandMAXsono supportati per il fast refresh.COUNT(*)deve essere specificato.- Le funzioni di aggregazione devono verificarsi solo come parte esterna dell'espressione. Cioè, aggregati come
AVG(AVG(x))orAVG(x)+AVG(x)non sono consentiti.- Per ogni aggregato come
AVG(expr), il corrispondenteCOUNT(expr)deve essere presente. Oracle raccomanda di specificareSUM(expr).Se- VARIANCE(expr)
STDDEV(expror) è specificato,deve essere specificato. Oracle raccomanda di utilizzareCOUNT(expr)andSUM(expr).SUM(expr * expr)la colonna nella query definente non può essere un'espressione complessa con colonne provenienti da più tabelle di base. Una possibile soluzione alternativa a questo è utilizzare una vista materializzata annidata.Se- Il
SELECTl'elenco deve contenere tutte- Il
SELECTle colonne.GROUPBYLa vista materializzata non si basa su una o più tabelle remote.- Se utilizzi un
- tipo di dato CHAR
nelle colonne di filtro di un registro di log della vista materializzata, i set di caratteri del sito master e della vista materializzata devono essere identici.Se la vista materializzata ha una delle seguenti, allora il refresh veloce è supportato solo per inserimenti DML convenzionali e caricamenti diretti.- Le viste materializzate con
- aggregati
MINorMAXLe viste materializzate che hanno- ma non
SUM(expr).Le viste materializzate senzaCOUNT(expr)- Tale vista materializzata è chiamata vista materializzata solo per inserimento.
COUNT(*)Una vista materializzata con
- è rinfrescabile rapidamente dopo dichiarazioni di cancellazione o DML misto se non contiene un
MAXorMINIl massimo/minimo rinfresco veloce dopo cancellazioni o DML misto non ha lo stesso comportamento del caso solo per inserimento. Elimina e ricalcola i valori max/min per i gruppi interessati. Devi essere consapevole del suo impatto sulle prestazioni.DOVEclausola.
Le viste materializzate con viste denominate o sottoquery nel- clausola possono essere rinfrescate rapidamente a condizione che le viste possano essere completamente unite. Per informazioni su quali viste verranno unite, consulta
DAOracle Database SQL Language Reference .- If there are no outer joins, you may have arbitrary selections and joins in the
DOVEclausola.- Le viste aggregate materializzate con join esterni possono essere aggiornate rapidamente dopo operazioni DML convenzionali e caricamenti diretti, a patto che solo la tabella esterna sia stata modificata. Inoltre, è necessario che esistano vincoli unici sulle colonne di join della tabella di join interna. Se ci sono join esterni, tutti i join devono essere connessi da
ANDe devono utilizzare l'operatore di uguaglianza (=)- Per le viste materializzate con
CUBE,ROLLUP, set di raggruppamento o concatenazione di essi, si applicano le seguenti restrizioni:
- Il
SELECTl'elenco deve contenere un discriminante di raggruppamento che può essere oppureGROUPING_IDfunzione su tutteGROUPBYespressioni oppureGROUPINGfunzioni una per ogniGROUPBYespressione. Ad esempio, se laGROUPBYclausola della vista materializzata è «GROUPBYCUBE(a, b)«, allora l'elenco deve contenere o «SELECTGROUPING_ID(a, b)» o «GROUPING(a)GROUPING(b)AND» affinché la vista materializzata sia aggiornata rapidamente.non deve risultare in raggruppamenti duplicati. Ad esempio, «GROUPBYGROUP BY a, ROLLUP(a, b)» non è aggiornabile rapidamente perché genera raggruppamenti duplicati «(a), (a, b), E (a)5.3.8.7 Restrizioni sul Refresh Veloce delle Viste Materializzate con UNION ALL«.Le viste materializzate con l'operatore
Materialized views with the
UNIONALLsupportano l'opzioneREFRESHFASTse sono soddisfatte le seguenti condizioni:
- La query definente deve avere l'operatore al livello più alto.
UNIONALLl'operatore non può essere incorporato in una sottoquery, con un'eccezione: L'Il
UNIONALLpuò essere in una sottoquery nellaUNIONALLclausola a condizione che la query definente sia della formaDASELECT * FROM(vista o sottoquery con) come nel seguente esempio:UNIONALLCREATE VIEW view_with_unionall AS (SELECT c.rowid crid, c.cust_id, 2 umarker FROM customers c WHERE c.cust_last_name = 'Smith' UNION ALL SELECT c.rowid crid, c.cust_id, 3 umarker FROM customers c WHERE c.cust_last_name = 'Jones');CREATE MATERIALIZED VIEW unionall_inside_view_mv REFRESH FAST ON DEMAND AS SELECT * FROM view_with_unionall;Nota che la vistaview_with_unionall
soddisfa i requisiti per un refresh veloce.Ogni blocco di query nella- Each query block in the
UNIONALLLa query deve soddisfare i requisiti di una vista materializzata aggiornata rapidamente con aggregati o di una vista materializzata aggiornata rapidamente con join.I registri delle viste materializzate appropriati devono essere creati sulle tabelle come richiesto per il corrispondente tipo di vista materializzata aggiornabile rapidamente.
Si noti che il database Oracle consente anche un caso speciale di vista materializzata su una singola tabella con join, a condizione che laROWIDcolonna sia stata inclusa nelSELECTelenco e nel registro delle viste materializzate. Questo è mostrato nella query di definizione della vistasoddisfa i requisiti per un refresh veloce..- Il
SELECTl'elenco di ogni query deve includere unUNIONALLindicatore, e laUNIONALLcolonna deve avere un valore numerico o stringa costante distinto in ciascunUNIONALLramo. Inoltre, la colonna dell'indicatore deve apparire nella stessa posizione ordinale nell'SELECTelenco di ciascun blocco di query. Vedi «» per ulteriori informazioni relative agliUNIONALLindicatori.- Alcune funzionalità, come gli join esterni, le query delle viste materializzate con aggregati solo per inserimento e le tabelle remote, non sono supportate per le viste materializzate con
UNIONALL. Si noti, tuttavia, che le viste materializzate utilizzate nella replicazione, che non contengono join o aggregati, possono essere aggiornate rapidamente quandoUNIONALLo tabelle remote sono utilizzate.- Il parametro di inizializzazione di compatibilità deve essere impostato su 9.2.0 o superiore per creare una vista materializzata aggiornata rapidamente con
UNIONALL.
Non voglio offendere i fan di Oracle, ma a giudicare dalla loro lista di limitazioni, sembra che questo meccanismo sia stato scritto non in un caso generale usando un modello, ma da migliaia di indiani, a ciascuno dei quali è stata data la libertà di scrivere la propria versione, e ognuno ha fatto ciò che ha potuto. Utilizzare questo meccanismo per la logica reale è come camminare su un campo minato. In qualsiasi momento si può attivare una mina, incappando in una delle limitazioni meno ovvie. Come funziona è un'altra questione, ma è al di fuori dell'ambito di questo articolo.
Microsoft SQL Server
Requisiti aggiuntivi
Oltre ai requisiti di opzioni SET e delle funzioni deterministiche, devono essere soddisfatti i seguenti requisiti:
- L'utente che esegue
CREATE INDEXdeve essere il proprietario della vista.- Quando si crea l'indice, l'opzione
IGNORE_DUP_KEYdeve essere impostata su OFF (l'impostazione predefinita).- Le tabelle devono essere riferite con nomi a due parti, schema.nome_tabella nella definizione della vista.
- Le funzioni definite dall'utente a cui si fa riferimento nella vista devono essere create utilizzando l'opzione
WITH SCHEMABINDING.Qualsiasi funzione definita dall'utente a cui si fa riferimento nella vista deve essere indicata con nomi a due parti,- Any user-defined functions referenced in the view must be referenced by two-part names, <schema>.<function>.
- La proprietà di accesso ai dati di una funzione definita dall'utente deve essere
NO SQL, e la proprietà di accesso esterno deve essereNO.- Le funzioni del runtime del linguaggio comune (CLR) possono apparire nell'elenco di selezione della vista, ma non possono far parte della definizione della chiave dell'indice cluster. Le funzioni CLR non possono apparire nella clausola WHERE della vista o nella clausola ON di un'operazione JOIN nella vista.
- Le funzioni e i metodi CLR dei tipi definiti dall'utente utilizzati nella definizione della vista devono avere le proprietà impostate come mostrato nella seguente tabella.
Proprietà
NotaDETERMINISTICO = VERO
Deve essere dichiarato esplicitamente come attributo del metodo del Microsoft .NET Framework.PRECISO = VERO
Deve essere dichiarato esplicitamente come attributo del metodo .NET Framework.ACCESSO AI DATI = NESSUNA SQL
Determinato impostando l'attributo DataAccess su DataAccessKind.None e l'attributo SystemDataAccess su SystemDataAccessKind.None.ACCESSO ESTERNO = NESSUNO
Questa proprietà predefinita è NO per le routine CLR.- La vista deve essere creata utilizzando il
WITH SCHEMABINDING.Qualsiasi funzione definita dall'utente a cui si fa riferimento nella vista deve essere indicata con nomi a due parti,- La vista deve fare riferimento solo alle tabelle di base che si trovano nello stesso database della vista. La vista non può fare riferimento ad altre viste.
- L'istruzione SELECT nella definizione della vista non deve contenere i seguenti elementi Transact-SQL:
CONTEGGIO
Funzioni ROWSET (OPENDATASOURCE,OPENQUERY,OPENROWSET, EOPENXML)
JOIN ESTERNI (LEFTLEFT,RIGHT, oINTERO)Tabella derivata (definita specificando un
SELECTistruzione nellaDAclausola)
Self-join
Specificando colonne utilizzandoSELECT *orSELECT .*
DISTINCT
STDEV,STDEVP,VAR,VARP, oAVG
Espressione comune della tabella (CTE)valore1, text, ntext, image, XML, o filestream colonne
Sottoquery
OVERclausola, che include funzioni di finestra di ranking o di aggregazionePredicati full-text (
CONTAINS,FREETEXT)
SUMfunzione che fa riferimento a un'espressione nullable
ORDER BYFunzione di aggregazione definita dall'utente CLR
TOP
CUBE,ROLLUP, oGROUPING SETSoperatori
MIN,MAX
UNION,EXCEPT, oINTERSECToperatori
TABLESAMPLEVariabili di tabella
OUTER APPLYorCROSS APPLY
PIVOT,UNPIVOTSet di colonne sparse
Funzioni tabulari inline (TVF) o funzioni di tabella a più dichiarazioni (MSTVF)
OFFSET
CHECKSUM_AGG1 La vista indicizzata può contenere valore colonne; tuttavia, tali colonne non possono essere incluse nella chiave dell'indice clustered.
- VARIANCE(expr)
GROUP BYè presente, la definizione della VISTA deve contenereCOUNT_BIG(*)e non deve contenereHAVING. QuesteGROUP BYrestrizioni si applicano solo alla definizione della vista indicizzata. Una query può utilizzare una vista indicizzata nel suo piano di esecuzione anche se non soddisfa questeGROUP BYrestrizioni.- Se la definizione della vista contiene un
GROUP BYclausola, la chiave dell'indice raggruppato unico può fare riferimento solo alle colonne specificate nelGROUP BYclausola.
Qui si vede che non sono stati coinvolti gli indiani, poiché hanno deciso di seguire lo schema "facciamo poco, ma bene". Cioè, hanno più miniere sul campo, ma la loro disposizione è più trasparente. Quello che rattrista di più è questa limitazione:
La vista deve fare riferimento solo alle tabelle di base che si trovano nello stesso database della vista. La vista non può fare riferimento ad altre viste.
Nella nostra terminologia, ciò significa che una funzione non può fare riferimento a un'altra funzione materializzata. Questo distrugge completamente tutta l'ideologia.
Anche questa limitazione (e proseguendo nel testo) riduce notevolmente le opzioni di utilizzo:
L'istruzione SELECT nella definizione della vista non deve contenere i seguenti elementi Transact-SQL:
CONTEGGIO
Funzioni ROWSET (OPENDATASOURCE,OPENQUERY,OPENROWSET, EOPENXML)
JOIN ESTERNI (LEFTLEFT,RIGHT, oINTERO)Tabella derivata (definita specificando un
SELECTistruzione nellaDAclausola)
Self-join
Specificando colonne utilizzandoSELECT *orSELECT .*
DISTINCT
STDEV,STDEVP,VAR,VARP, oAVG
Espressione comune della tabella (CTE)valore1, text, ntext, image, XML, o filestream colonne
Sottoquery
OVERclausola, che include funzioni di finestra di ranking o di aggregazionePredicati full-text (
CONTAINS,FREETEXT)
SUMfunzione che fa riferimento a un'espressione nullable
ORDER BYFunzione di aggregazione definita dall'utente CLR
TOP
CUBE,ROLLUP, oGROUPING SETSoperatori
MIN,MAX
UNION,EXCEPT, oINTERSECToperatori
TABLESAMPLEVariabili di tabella
OUTER APPLYorCROSS APPLY
PIVOT,UNPIVOTSet di colonne sparse
Funzioni tabulari inline (TVF) o funzioni di tabella a più dichiarazioni (MSTVF)
OFFSET
CHECKSUM_AGG
Sono vietati OUTER JOIN, UNION, ORDER BY e altro. Forse sarebbe stato più semplice indicare cosa è consentito usare, piuttosto che cosa non è permesso. La lista sarebbe stata probabilmente molto più corta.
In sintesi: un'enorme serie di limitazioni in ciascun RDBMS (noterò commerciale) vs nessuna (eccetto una logica, non tecnica) nella tecnologia LGPL. Tuttavia, va notato che implementare questo meccanismo nella logica relazionale è un po' più complesso che nella funzionale descritta.
Implementazione
Come funziona? Come "macchina virtuale" viene utilizzato PostgreSQL. All'interno c'è un algoritmo complesso che si occupa della costruzione delle query. Ecco . E non si tratta semplicemente di un ampio insieme di euristiche con una miriade di if. Quindi, se avete un paio di mesi per approfondire, potete provare a capire l'architettura.
Funziona in modo efficiente? Abbastanza efficacemente. Purtroppo, è difficile dimostrarlo. Posso solo dire che, se consideriamo migliaia di richieste in grandi applicazioni, in media sono più efficienti rispetto a un buon sviluppatore. Un ottimo programmatore SQL può scrivere qualsiasi query in modo più efficiente, ma su mille richieste non avrà né la motivazione né il tempo per farlo. L'unica cosa che posso fornire ora come prova di efficienza è che su una piattaforma costruita su questo DBMS operano diversi progetti , nei quali ci sono migliaia di diverse funzioni MATERIALIZED, con migliaia di utenti e database da terabyte con centinaia di milioni di record, funzionanti su un server a due processori comune. Tuttavia, chiunque può verificare/smentire l'efficienza scaricando e PostgreSQL, il logging delle query SQL e provando a modificare la logica e i dati.
Negli articoli seguenti, parlerò anche di come impostare limiti sulle funzioni, lavorare con le sessioni delle modifiche e molto altro.
Fonte: habr.com
