
Nella parte precedente Ho descritto il concetto e l'implementazione di un database basato su funzioni, anziché su tabelle e campi come nei database relazionali. Sono stati presentati numerosi esempi che mostrano i vantaggi di questo approccio rispetto al classico. Molti hanno trovato questi esempi poco convincenti.
In questo articolo mostrerò come un tale concetto consenta di bilanciare rapidamente e comodamente le operazioni di scrittura e lettura in un database senza alcuna modifica della logica di funzionamento. Funzionalità simile è stata tentata di essere implementata nei moderni DBMS commerciali (in particolare, Oracle e Microsoft SQL Server). Alla fine dell'articolo mostrerò che ciò che hanno ottenuto, per usare un eufemismo, non è stato molto soddisfacente.
Descrizione
Come in precedenza, per una migliore comprensione inizierò la descrizione con degli esempi. Supponiamo di dover implementare la logica che restituisce un elenco di reparti con il numero di dipendenti in essi e il loro stipendio totale.
In un database funzionale ciò apparirà come segue:
CLASS Dipartimento ‘Dipartimento’;
nome ‘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) =
RAGGRUPPA somma 1 SE dipartimento (Dipendente e) = d;
salarySum ‘Stipendio totale’ (Department d) =
SOMMA GRUPPO stipendio (Dipendente e) SE reparto(e) = d;
SELECT name(Department d), countEmployees(d), salarySum(d);
La complessità di esecuzione di questa query in qualsiasi DBMS sarà equivalente a O(numero di dipendenti), poiché per questo calcolo è necessario scansionare l'intera tabella dei dipendenti e poi raggrupparli per reparto. Ci sarà anche una piccola aggiunta (supponiamo che ci siano molti più dipendenti che reparti) a seconda del piano scelto O(log numero di dipendenti) o O(numero di reparti) per il raggruppamento e altro.
È chiaro che le spese generali per l'esecuzione possono variare nei diversi DBMS, ma la complessità non cambierà.
Nell'implementazione proposta, il database funzionale genererà una sottoquery che calcolerà i valori necessari per il reparto e poi farà un JOIN con la tabella dei reparti per ottenere il nome. Tuttavia, per ogni funzione alla dichiarazione è possibile impostare 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 tratterà già di un accesso al campo precalcolato.
In particolare, se si imposta MATERIALIZED per le funzioni conteggioDipendenti e sommaStipendi, quindi nella tabella con l'elenco dei reparti verranno aggiunti due campi in cui saranno memorizzati il numero di dipendenti e il loro stipendio totale. Ad ogni modifica dei dipendenti, dei loro stipendi o dell'appartenenza ai reparti, il sistema modificherà automaticamente i valori di questi campi. La query sopra citata accederà direttamente a questi campi e verrà eseguita in O(numero di reparti).
Quali sono le limitazioni? Solo una: questa funzione deve avere un numero finito di valori di input, per i quali il suo valore è definito. Altrimenti, non sarà possibile costruire una tabella che memorizzi tutti i suoi valori, poiché non può esserci una tabella con un numero infinito di righe.
Esempio:
conteggioDipendenti ‘Numero di dipendenti con stipendio > N’ (Dipartimento d, NUMERIC[10,2] N) =
GRUPPO SOMMA 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 può essere impostata come MATERIALIZZATA. Così, questa è una limitazione logica, non tecnica (cioè non perché non siam stati in grado di implementarla). In tutto il resto — nessuna limitazione. Possono essere utilizzate aggregazioni, ordinamenti, AND e OR, PARTITION, ricorsioni, ecc.
Ad esempio, nel compito 2.2 dell'articolo precedente si può impostare MATERIALIZZATA su entrambe le funzioni:
acquistato 'Acquistato' (Cliente c, Prodotto p, INTERO y) =
SOMMA GRUPPO somma(Dettaglio d) SE
cliente(ordina(d)) = c E
prodotto(d) = p E
estraeAnno(data(ord(d))) = y MATERIALIZZATO;
valutazione 'Valutazione' (Cliente c, Prodotto p, INTERO y) =
PARTITION SOMMA 1 ORDINA DESC comprato(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 INTEGER, aggiungerà due campi e aggiornerà i valori in essi in caso di modifiche. Negli accessi successivi a queste funzioni non verrà effettuato alcun calcolo, ma verranno letti i valori dai campi corrispondenti.
Attraverso questo meccanismo è possibile, 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):
parent = DATA Gruppo (Gruppo);
Nella base dati funzionale la logica delle ricorsioni può essere definita nel seguente modo:
livello (Gruppo figlio, Gruppo genitore) = RECURSIONE 1l SE il figlio è un Gruppo E il genitore == figlio
FASE 2l SE il genitore == genitore($parent);
èGenitore (Gruppo figlio, Gruppo genitore) = VERO SE livello(figlio, genitore) MATERIALIZZATO;
Poiché per la funzione isParent è stato impostato MATERIALIZZATA, 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 record in questa tabella sarà uguale al numero di gruppi, moltiplicato per la profondità media dell'albero. Se è necessario, ad esempio, contare il numero di discendenti di un determinato gruppo, è possibile accedere a questa funzione:
numeroDiBambini (Gruppo g) = SOMMA GRUPPO 1 SE isParent(Gruppo bambino, g);
Non ci sarà alcun CTE nella query SQL. Invece, ci sarà semplicemente un GROUP BY.
Con questo meccanismo è possibile effettuare facilmente la denormalizzazione del database se necessario:
CLASS Order 'Ordine';
data 'Data' = DATA DATE (Ordine);
CLASS OrderDetail 'Riga dell'ordine';
order 'Ordine' = DATA Order (OrderDetail);
date 'Data' (OrderDetail d) = date(order(d)) MATERIALIZED INDEXED;
Quando si chiama la funzione date per la riga dell'ordine sarà letta dalla tabella con le righe d'ordine il campo su cui esiste un indice. Quando la data dell'ordine viene modificata, il sistema ri-calcolerà automaticamente la data denormalizzata nella riga.
Vantaggi
A cosa serve tutto questo meccanismo? Nelle basi di dati classiche, senza riscrivere le query, lo sviluppatore o l'amministratore DBA possono solo modificare gli indici, definire le statistiche e suggerire al pianificatore di query come eseguirle (e i suggerimenti sono disponibili solo in basi di dati commerciali). Per quanto si sforzino, non potranno eseguire la prima query nell'articolo in O (numero di reparti) senza modificare le query e senza aggiungere trigger. Nello schema proposto, nella fase di sviluppo, non c'è bisogno di preoccuparsi della struttura di memorizzazione dei dati e di quali aggregazioni utilizzare. Tutto ciò può essere facilmente modificato al volo direttamente in fase di utilizzo.
Nella pratica appare in questo modo. Alcune persone sviluppano direttamente la logica sulla base del compito assegnato. Non si occupano né di algoritmi e delle loro complessità, né dei piani di esecuzione, né dei tipi di join, né di qualsiasi altro aspetto tecnico. Queste persone sono più analisti di business che sviluppatori. Successivamente, tutto viene inviato per test o utilizzo. Viene attivata la registrazione delle query lunghe. Quando viene scoperta una query lunga, altre persone (più tecniche — essenzialmente DBA) prendono la decisione di attivare MATERIALIZED su una certa funzione intermedia. In questo modo si rallenta un po' la scrittura (poiché è necessario aggiornare un campo aggiuntivo nella transazione). Tuttavia, si accelera notevolmente non solo questa query, ma anche tutte le altre che utilizzano questa funzione. In questo caso, la decisione su quale funzione materializzare viene presa relativamente facilmente. Due parametri principali: il numero possibile di valori di input (sono esattamente tante le registrazioni che saranno 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 all'interno di una transazione, ma solo su richiesta (con restrizioni molto severe), quindi non la prendiamo in considerazione. Tuttavia, hanno diversi problemi che limitano notevolmente il loro utilizzo.
In primo luogo, è possibile abilitare la materializzazione solo se è già stata creata una VIEW ordinaria. Altrimenti, sarà necessario riscrivere tutte le altre query che fanno riferimento alla nuova vista creata per utilizzare questa materializzazione. Oppure lasciar tutto com'è, ma sarebbe almeno inefficace se ci sono dati già pre-calcolati che molte query non utilizzano sempre, ma ricalcolano.
In secondo luogo, hanno un numero enorme di restrizioni:
Oracle
5.3.8.4 Restrizioni generali su Fast Refresh
La query definente della vista materializzata è limitata come segue:
- La vista materializzata non deve contenere riferimenti a espressioni non ripetitive come
SYSDATEandROWNUM.- La vista materializzata non deve contenere riferimenti a
RAWorLONGRAWtipi di dati.- Non può contenere una
SELECTsottoselezione di tipo lista.- Non può contenere funzioni analitiche (ad esempio,
RANK) nellaSELECTclausola.- Non può fare riferimento a una tabella su cui è definito un
XMLIndexindice.- Non può contenere una
MODELclausola.- Non può contenere una
clausola HAVING con una sottoselezione.Non può contenere query annidate che hanno- , o
ANY,ALL[START WITH …] CONNECT BYNOTESISTE.- Non può contenere una
Non può contenere più tavole dettagliate in siti diversi.clausola.- Le viste materializzate non possono avere tabelle dettagliate remote.
ACCESOCOMMITLe viste materializzate annidate devono avere un join o un aggregato.- Le viste di join materializzate e le viste aggregate materializzate con una
- clausola GROUP
BYnon possono selezionare da una tabella organizzata per indice.5.3.8.5 Restrizioni su Fast Refresh su Viste Materializzate con Solo JoinsLe query definite per le viste materializzate con solo join e senza aggregati hanno le seguenti restrizioni sul fast refresh:
Tutte le restrizioni da«
- Restrizioni generali su Fast Refresh«.
- clausole o aggregati.
BYnon possono selezionare da una tabella organizzata per indice.Gli Rowid di tutte le tabelle nella- lista devono apparire nella
DAlista della query.SELECTI registri delle viste materializzate devono esistere con rowid per tutte le tabelle di base nella- Non puoi creare una vista materializzata con refresh veloce da più tabelle con join semplici che includono una colonna di tipo oggetto nella
DAI registri delle viste materializzate devono esistere con rowid per tutte le tabelle di base nella- istruzione.
SELECTInoltre, il metodo di refresh scelto non sarà ottimale se:La query definente utilizza un join esterno che si comporta come un join interno. Se la query definente contiene tale join, considera di riscrivere la query definente per contenere un join interno.
- La lista della vista materializzata contiene espressioni su colonne provenienti da più tabelle.
- Il
SELECT5.3.8.6 Restrizioni su Fast Refresh su Viste Materializzate con AggregatiLe query definite per le viste materializzate con aggregati o join hanno le seguenti restrizioni sul fast refresh:
Il fast refresh è supportato per entrambe le
- Restrizioni generali su Fast Refresh«.
viste materializzate per RICHIESTA, tuttavia si applicano le seguenti restrizioni:
ACCESOCOMMITandACCESOTutte le tabelle nella vista materializzata devono avere registri di vista materializzata, e i registri di vista materializzata devono:Contenere tutte le colonne dalla tabella a cui si fa riferimento nella vista materializzata.
- Specificare con
- ROWID
- INCLUDENDO
NUOVOandSpecificare laSEQUENZAVALUES.- clausola se ci si aspetta che la tabella abbia una combinazione di inserimenti/caricamenti diretti, cancellazioni e aggiornamenti.
SoloSOMMA- COUNT
STDDEV,VARIANZA,AVG,MAX,sono supportati per il refresh veloce.,MINandCOUNT(*)deve essere specificato.COUNT(*)deve essere specificato.- Le funzioni aggregate devono apparire solo come la parte esterna dell'espressione. Vale a dire, le aggregazioni come
AVG(AVG(x))orAVG(x)+AVG(x)non sono consentite.- Per ogni aggregato come
AVG(expr), deve essere presente il corrispondenteCOUNT(expr). Oracle consiglia di specificareSUM(expr).- Se
VARIANCE(expr)orSTDDEV(expr) è specificato,COUNT(expr)andSUM(expr)deve essere specificato. Oracle consiglia di specificareSUM(expr *expr).- Il
SELECTla colonna nella query definente non può essere un'espressione complessa con colonne provenienti da più tabelle di base. Un possibile lavoro attorno a questo è utilizzare una vista materializzata annidata.- Il
SELECTl'elenco deve contenere tutteBYnon possono selezionare da una tabella organizzata per indice.le colonne.- La vista materializzata non si basa su una o più tabelle remote.
- Se utilizzi un
CHARtipo di dati nelle colonne di filtro di una vista materializzata, i set di caratteri del sito master e della vista materializzata devono essere gli stessi.- Se la vista materializzata ha uno dei seguenti, allora l'aggiornamento veloce è supportato solo su inserimenti DML convenzionali e caricamenti diretti.
- Le viste materializzate con
MINorCOUNT(*)aggregati- Le viste materializzate che hanno
SUM(expr)ma nonCOUNT(expr)- Le viste materializzate senza
COUNT(*)Tale vista materializzata è chiamata vista materializzata solo per inserimenti.
- Una vista materializzata con
COUNT(*)orMINè aggiornabile rapidamente dopo cancellazioni o istruzioni DML miste se non ha unDOVEclausola.
La velocità massima/minima di aggiornamento rapido dopo cancellazioni o DML misti non ha lo stesso comportamento del caso di solo inserimento. Elimina e ricalcola i valori max/min per i gruppi interessati. Devi essere consapevole del suo impatto sulle prestazioni.- Le viste materializzate con viste nominate o sottoquery nella
DAclausola possono essere aggiornate rapidamente, a condizione che le viste possano essere completamente unite. Per informazioni su quali viste si uniranno, vedere .- Se non ci sono join esterni, puoi avere selezioni e join arbitrari nella
DOVEclausola.- Le viste aggregate materializzate con join esterni sono aggiornabili rapidamente dopo DML convenzionali e caricamenti diretti, a condizione che solo la tabella esterna sia stata modificata. Inoltre, devono esistere vincoli unici sulle colonne di join della tabella di join interna. Se ci sono join esterni, tutti i join devono essere collegati da
Es e devono utilizzare l'operatore di uguaglianza (=).- Per le viste materializzate con
CUBE,ROLLUP, insiemi di raggruppamento o concatenazione di essi, si applicano le seguenti restrizioni:
- Il
SELECTl'elenco deve contenere un distintivo di raggruppamento che può essere o unGROUPING_IDfunzione su tutteBYnon possono selezionare da una tabella organizzata per indice.le espressioni ofunzioni GROUPING, una per ogniespressione. Ad esempio, se laBYnon possono selezionare da una tabella organizzata per indice.clausola della vista materializzata è «BYnon possono selezionare da una tabella organizzata per indice.CUBE(a, b)BYnon possono selezionare da una tabella organizzata per indice.«, allora l'elenco dovrebbe contenere o «GROUPING_ID(a, b)SELECT» o «GROUPING(a)GROUPING(b)» affinché la vista materializzata sia aggiornabile rapidamente.Enon dovrebbe risultare in raggruppamenti duplicati. Ad esempio, «GROUP BY a, ROLLUP(a, b)BYnon possono selezionare da una tabella organizzata per indice.» non è aggiornabile rapidamente perché risulta in raggruppamenti duplicati «(a), (a, b), E (a)5.3.8.7 Restrizioni sull'aggiornamento rapido delle viste materializzate con UNION ALLLe viste materializzate con il«.operatore set supportano l'opzione
REFRESH
UNIONEALLFASTse le seguenti condizioni sono soddisfatte:La query definente deve avere l'operatoreal livello superiore.
- l'operatore non può essere incorporato all'interno di una sottoquery, con una eccezione: Il
UNIONEALLpuò trovarsi in una sottoquery nellaIl
UNIONEALLclausola a condizione che la query definente sia della formaUNIONEALLSELECT * FROMDA(vista o sottoquery con) come nell'esempio seguente:CREATE 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;UNIONEALLNota che la vistaview_with_unionallsoddisfa i requisiti per un aggiornamento rapido.
Ogni blocco di query nellaquery deve soddisfare i requisiti di una vista materializzata rapidamente aggiornabile con aggregati o una vista materializzata rapidamente aggiornabile con join.- Devono essere creati i log delle viste materializzate appropriati sulle tabelle come richiesto per il tipo corrispondente di vista materializzata rapidamente aggiornabile.
UNIONEALLntextfilestream
Nota che l'Oracle Database consente anche il caso speciale di una vista materializzata su una singola tabella con join a condizione cheNUOVOla colonna sia stata inclusa nellaSELECTlista e nel log della vista materializzata. Questo è mostrato nella query di definizione della vistaOgni blocco di query nella.- Il
SELECTla lista di ogni query deve includere unUNIONEALLmarcatore, e laUNIONEALLcolonna deve avere un valore numerico o di stringa costante distinto in ciascunUNIONEALLramo. Inoltre, la colonna marcatore deve apparire nella stessa posizione ordinale nellaSELECTlista di ogni blocco di query. Vedi «» per ulteriori informazioni riguardo aiUNIONEALLmark. Alcune funzionalità come join esterni, query di viste materializzate aggregate solo inserite e tabelle remote non sono supportate per le viste materializzate con- . Nota, tuttavia, che le viste materializzate utilizzate nella replicazione, che non contengono join o aggregati, possono essere aggiornate rapidamente quando
UNIONEALLo tabelle remote sono utilizzate.UNIONEALLIl parametro di inizializzazione della compatibilità deve essere impostato su 9.2.0 o superiore per creare una vista materializzata rapidamente aggiornabile con- Non voglio offendere i fan di Oracle, ma a giudicare dalla loro lista di limitazioni, si ha l'impressione che questo meccanismo sia stato scritto non in un caso generale, utilizzando un qualche modello, ma da migliaia di indiani, dove a ciascuno è stato dato di scrivere la propria branca, e ognuno di loro ha fatto quel che ha potuto. Utilizzare questo meccanismo per la logica reale è come camminare su un campo minato. In qualsiasi momento si può attivare una mina, cadendo su una delle limitazioni non ovvie. Come funziona — è anch'esso un argomento separato, ma si trova al di fuori dell'ambito di questo articolo.
UNIONEALL.
Requisiti Aggiuntivi
Microsoft SQL Server
Oltre alle opzioni SET e ai requisiti delle funzioni deterministiche, devono essere soddisfatti i seguenti requisiti:
L'utente che esegue
- deve essere il proprietario della vista.
CREATE INDEXQuando crei l'indice, l'- IGNORE_DUP_KEY
opzione deve essere impostata su OFF (l'impostazione predefinita).Le tabelle devono essere referenziate con nomi a due parti,- nella definizione della vista. schema.tablename Le funzioni definite dall'utente referenziate nella vista devono essere create utilizzando l'
- OPZIONE WITH SCHEMABINDING.
Qualsiasi funzione definita dall'utente referenziata nella vista deve essere referenziata con nomi a due parti,<schema>- <funzione> La proprietà di accesso ai dati di una funzione definita dall'utente deve essere.NO SQL.
- , e la proprietà di accesso esterno deve essere
NOLe funzioni del runtime di linguaggio comune (CLR) possono apparire nella lista di selezione della vista, ma non possono far parte della definizione della chiave dell'indice clusterizzato. Le funzioni CLR non possono apparire nella clausola WHERE della vista o nella clausola ON di un'operazione JOIN nella vista.Le funzioni CLR e i metodi dei tipi definiti dall'utente CLR utilizzati nella definizione della vista devono avere le proprietà impostate come mostrato nella seguente tabella..- DETERMINISTICO = TRUE
- Deve essere dichiarato esplicitamente come attributo del metodo Microsoft .NET Framework.
Proprietà
NotePRECISO = TRUE
Deve essere dichiarato esplicitamente come attributo del metodo .NET Framework.ACCESSO AI DATI = NO SQL
Determinato impostando l'attributo DataAccess su DataAccessKind.None e l'attributo SystemDataAccess su SystemDataAccessKind.None.ACCESSO ESTERNO = NO
Questa proprietà per impostazione predefinita è NO per le routine CLR.La vista deve essere creata utilizzando il
La vista deve riferire solo a tabelle di base che si trovano nello stesso database della vista. La vista non può fare riferimento ad altre viste.- La dichiarazione SELECT nella definizione della vista non deve contenere i seguenti elementi Transact-SQL:
Qualsiasi funzione definita dall'utente referenziata nella vista deve essere referenziata con nomi a due parti,<schema>- funzioni ROWSET (
- OPENDATASOURCE
VARIANZA
OPENQUERYOPENROWSET,, E,OPENXMLOUTERjoin ()
SINISTRODESTRATabella derivata (definita specificando una,dichiarazione nella[START WITH …] CONNECT BYFULL)clausola)
SELECTSelf-joinDASpecificando colonne utilizzando
SELECT *
SELECT <nome_tabella>.*STDEVorSTDEVP
DISTINCT
VAR,VARP,Espressione di tabella comune (CTE),ntext[START WITH …] CONNECT BYAVG
filestreamfloat1, text, ntext, image, XML[START WITH …] CONNECT BY filestream colonne
Sottoquery
OVERclausola, che include funzioni di finestra di ranking o aggregazionePredicati di full-text (
CONTENERE,FREETEXT)
STDDEVfunzione che si riferisce a un'espressione nullable
ORDER BYfunzione di aggregazione definita dall'utente CLR
TOP
CUBE,ROLLUP[START WITH …] CONNECT BYSET DI GRUPPIoperatori
MIN,COUNT(*)
UNIONE,ECCETTO[START WITH …] CONNECT BYINTERSEZIONEoperatori
TABLESAMPLEVariabili di tabella
OUTER APPLYorCROSS APPLY
PIVOT,UNPIVOTSet di colonne sparse
Funzioni a valore di tabella inline (TVF) o multichiamata (MSTVF)
OFFSET
CHECKSUM_AGG1 La vista indicizzata può contenere float colonne; tuttavia, tali colonne non possono essere incluse nella chiave dell'indice cluster.
- Se
GROUP BY.è presente, la definizione della VISTA deve contenereCOUNT_BIG(*)e non deve contenereclausola HAVING con una sottoselezione.. QuesteGROUP BY.restrizioni 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 BY.restrizioni.- Se la definizione della vista contiene una
GROUP BY.clausola, la chiave dell'indice cluster unico può fare riferimento solo alle colonne specificate nellaGROUP BY.clausola.
Qui si vede che non hanno attirato indù, poiché hanno deciso di seguire lo schema “facciamo poco, ma bene”. Cioè, hanno più mine sul campo, ma la loro posizione è più trasparente. Ciò che delude di più è questa limitazione:
funzioni ROWSET (
Nella nostra terminologia questo significa che la funzione non può riferirsi a un'altra funzione materializzata. Questo tronca completamente l'ideologia.
Anche questa limitazione (e così via nel testo) riduce notevolmente le possibilità d'uso:
OPENDATASOURCE
VARIANZA
OPENQUERYOPENROWSET,, E,OPENXMLOUTERjoin ()
SINISTRODESTRATabella derivata (definita specificando una,dichiarazione nella[START WITH …] CONNECT BYFULL)clausola)
SELECTSelf-joinDASpecificando colonne utilizzando
SELECT *
SELECT <nome_tabella>.*STDEVorSTDEVP
DISTINCT
VAR,VARP,Espressione di tabella comune (CTE),ntext[START WITH …] CONNECT BYAVG
filestreamfloat1, text, ntext, image, XML[START WITH …] CONNECT BY filestream colonne
Sottoquery
OVERclausola, che include funzioni di finestra di ranking o aggregazionePredicati di full-text (
CONTENERE,FREETEXT)
STDDEVfunzione che si riferisce a un'espressione nullable
ORDER BYfunzione di aggregazione definita dall'utente CLR
TOP
CUBE,ROLLUP[START WITH …] CONNECT BYSET DI GRUPPIoperatori
MIN,COUNT(*)
UNIONE,ECCETTO[START WITH …] CONNECT BYINTERSEZIONEoperatori
TABLESAMPLEVariabili di tabella
OUTER APPLYorCROSS APPLY
PIVOT,UNPIVOTSet di colonne sparse
Funzioni a valore di tabella inline (TVF) o multichiamata (MSTVF)
OFFSET
CHECKSUM_AGG
Gli OUTER JOIN, UNION, ORDER BY e altri sono vietati. Forse sarebbe stato più semplice specificare cosa è consentito utilizzare piuttosto che cosa non è consentito. La lista sarebbe stata probabilmente molto più breve.
In sintesi: un enorme insieme di limitazioni in ogni (osserva commerciale) DBMS vs nessuna (eccetto una logica, non tecnica) nella tecnologia LGPL. Tuttavia, va notato che implementare questo meccanismo nella logica relazionale è un po' più complicato rispetto a quanto descritto nella logica funzionale.
Implementazione
Come funziona? Come "macchina virtuale" viene utilizzato PostgreSQL. All'interno c'è un algoritmo complesso che si occupa della costruzione delle query. Ecco . E lì non ci sono solo un gran numero di euristiche con molte if. Quindi, se hai un paio di mesi da dedicare allo studio, puoi provare a capire l'architettura.
Funziona in modo efficace? Abbastanza efficace. Purtroppo, dimostrare questo è difficile. Posso solo dire che considerando 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 prova di efficacia che posso fornire ora è che su una piattaforma basata su questo DBMS funzionano diversi progetti , che ospitano migliaia di funzioni MATERIALIZED, con migliaia di utenti e database da terabyte con centinaia di milioni di record, tutti operanti su un comune server a due processori. Comunque, chiunque può verificare o smentire l'efficacia scaricando e PostgreSQL, attivando il logging delle query SQL e provando a modificare la logica e i dati.
Nei prossimi articoli, parlerò anche di come è possibile applicare restrizioni alle funzioni, gestire le sessioni di modifica e molto altro.
Fonte: habr.com
