Bilanciamento di lettura e scrittura nel database

Bilanciamento di lettura e scrittura nel database
Nella precedente articolo 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 SYSDATE and ROWNUM.
  • La materialized view non deve contenere riferimenti a RAW or LONG RAW tipi di dati.
  • Non può contenere una SELECT subquery di elenco.
  • Non può contenere funzioni analitiche (per esempio, RANK) nella SELECT clausola.
  • Non può fare riferimento a una tabella su cui è definito un XMLIndex indice.
  • Non può contenere una MODEL clausola.
  • Non può contenere una HAVING clausola con una subquery.
  • Non può contenere query nidificate che abbiano ANY, ALL, o NOT EXISTS.
  • Non può contenere una [START WITH …] CONNECT BY clausola.
  • Non può contenere più tabelle di dettaglio in siti diversi.
  • ON COMMIT Le 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 GROUP BY non 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 «Restrizioni Generali sul Fast Refresh«.
  • Non possono avere GROUP BY clausole o aggregati.
  • I rowid di tutte le tabelle nella DA lista devono apparire nella SELECT lista della query.
  • I log delle viste materializzate devono esistere con rowid per tutte le tabelle di base nel DA lista 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 SELECT dichiarazione.

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 SELECT la 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:

Il fast refresh è supportato per entrambe le ON COMMIT and ON DEMAND viste 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 ROWID and INCLUDING NEW VALUES.
    • Specificare la SEQUENCE clausola se ci si aspetta che la tabella abbia un mix di inserimenti/caricamenti diretti, cancellazioni e aggiornamenti.

  • Solo SUM, CONTEGGIO, AVG, STDDEV, VARIANCE, MIN and MAX sono 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)) or AVG(x)+ AVG(x) non sono consentiti.
  • Per ogni aggregato come AVG(expr), il corrispondente COUNT(expr) deve essere presente. Oracle raccomanda di specificare SUM(expr). Se
  • VARIANCE(expr) STDDEV(expr or ) è specificato,deve essere specificato. Oracle raccomanda di utilizzare COUNT(expr) and SUM(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 SELECT l'elenco deve contenere tutte
  • Il SELECT le colonne. GROUP BY La 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 MIN or MAX Le viste materializzate che hanno
    • ma non SUM(expr). Le viste materializzate senza COUNT(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 MAX or MIN Il 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. DOVE clausola.
    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 DA Oracle Database SQL Language Reference Se non ci sono join esterni, puoi avere selezioni e join arbitrari in.
  • If there are no outer joins, you may have arbitrary selections and joins in the DOVE clausola.
  • 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 SELECT l'elenco deve contenere un discriminante di raggruppamento che può essere oppure GROUPING_ID funzione su tutte GROUP BY espressioni oppure GROUPING funzioni una per ogni GROUP BY espressione. Ad esempio, se la GROUP BY clausola della vista materializzata è «GROUP BY CUBE(a, b)«, allora l'elenco deve contenere o « SELECT GROUPING_ID(a, b)» o «GROUPING(a)GROUPING(b) AND » affinché la vista materializzata sia aggiornata rapidamente.non deve risultare in raggruppamenti duplicati. Ad esempio, «
    • GROUP BY GROUP 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 UNION ALL supportano l'opzione REFRESH FAST se sono soddisfatte le seguenti condizioni:

  • La query definente deve avere l'operatore al livello più alto. UNION ALL l'operatore non può essere incorporato in una sottoquery, con un'eccezione: L'

    Il UNION ALL può essere in una sottoquery nella UNION ALL clausola a condizione che la query definente sia della forma DA SELECT * FROM (vista o sottoquery con ) come nel seguente esempio: UNION ALLCREATE 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 vista
    

    view_with_unionall soddisfa i requisiti per un refresh veloce. Ogni blocco di query nella

  • Each query block in the UNION ALL La 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 la ROWID colonna sia stata inclusa nel SELECT elenco e nel registro delle viste materializzate. Questo è mostrato nella query di definizione della vista soddisfa i requisiti per un refresh veloce..

  • Il SELECT l'elenco di ogni query deve includere un UNION ALL indicatore, e la UNION ALL colonna deve avere un valore numerico o stringa costante distinto in ciascun UNION ALL ramo. Inoltre, la colonna dell'indicatore deve apparire nella stessa posizione ordinale nell' SELECT elenco di ciascun blocco di query. Vedi «Indicatore UNION ALL e Riscrittura Query» per ulteriori informazioni relative agli UNION ALL indicatori.
  • 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 UNION ALL. Si noti, tuttavia, che le viste materializzate utilizzate nella replicazione, che non contengono join o aggregati, possono essere aggiornate rapidamente quando UNION ALL o 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 UNION ALL.

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 INDEX deve essere il proprietario della vista.
  • Quando si crea l'indice, l'opzione IGNORE_DUP_KEY deve 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 essere NO.
  • 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à
    Nota

    DETERMINISTICO = 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, E OPENXML)
    JOIN ESTERNI ( LEFTLEFT, RIGHT, o INTERO)

    Tabella derivata (definita specificando un SELECT istruzione nella DA clausola)
    Self-join
    Specificando colonne utilizzando SELECT * or SELECT .*

    DISTINCT
    STDEV, STDEVP, VAR, VARP, o AVG
    Espressione comune della tabella (CTE)

    valore1, text, ntext, image, XML, o filestream colonne
    Sottoquery
    OVER clausola, che include funzioni di finestra di ranking o di aggregazione

    Predicati full-text (CONTAINS, FREETEXT)
    SUM funzione che fa riferimento a un'espressione nullable
    ORDER BY

    Funzione di aggregazione definita dall'utente CLR
    TOP
    CUBE, ROLLUP, o GROUPING SETS operatori

    MIN, MAX
    UNION, EXCEPT, o INTERSECT operatori
    TABLESAMPLE

    Variabili di tabella
    OUTER APPLY or CROSS APPLY
    PIVOT, UNPIVOT

    Set di colonne sparse
    Funzioni tabulari inline (TVF) o funzioni di tabella a più dichiarazioni (MSTVF)
    OFFSET

    CHECKSUM_AGG

    1 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 contenere COUNT_BIG(*) e non deve contenere HAVING. Queste GROUP 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 queste GROUP BY restrizioni.
  • Se la definizione della vista contiene un GROUP BY clausola, la chiave dell'indice raggruppato unico può fare riferimento solo alle colonne specificate nel GROUP BY clausola.

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, E OPENXML)
JOIN ESTERNI ( LEFTLEFT, RIGHT, o INTERO)

Tabella derivata (definita specificando un SELECT istruzione nella DA clausola)
Self-join
Specificando colonne utilizzando SELECT * or SELECT .*

DISTINCT
STDEV, STDEVP, VAR, VARP, o AVG
Espressione comune della tabella (CTE)

valore1, text, ntext, image, XML, o filestream colonne
Sottoquery
OVER clausola, che include funzioni di finestra di ranking o di aggregazione

Predicati full-text (CONTAINS, FREETEXT)
SUM funzione che fa riferimento a un'espressione nullable
ORDER BY

Funzione di aggregazione definita dall'utente CLR
TOP
CUBE, ROLLUP, o GROUPING SETS operatori

MIN, MAX
UNION, EXCEPT, o INTERSECT operatori
TABLESAMPLE

Variabili di tabella
OUTER APPLY or CROSS APPLY
PIVOT, UNPIVOT

Set 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 codice sorgente. 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 sistemi ERP, 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 la piattaforma e PostgreSQL, attivando 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

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster