Bilanciamento delle operazioni di scrittura e lettura nel database

Bilanciamento delle operazioni di scrittura e lettura nel database
Nella parte precedente abbiamo chiarito il corretto completamento dei programmi che utilizzano il mediastreamer. 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 SYSDATE and ROWNUM.
  • La vista materializzata non deve contenere riferimenti a RAW or LONG RAW tipi di dati.
  • Non può contenere una SELECT sottoselezione di tipo lista.
  • Non può contenere funzioni analitiche (ad 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 clausola HAVING con una sottoselezione. Non può contenere query annidate che hanno
  • , o ANY, ALL[START WITH …] CONNECT BY NOT ESISTE.
  • Non può contenere una Non può contenere più tavole dettagliate in siti diversi. clausola.
  • Le viste materializzate non possono avere tabelle dettagliate remote.
  • ACCESO COMMIT Le viste materializzate annidate devono avere un join o un aggregato.
  • Le viste di join materializzate e le viste aggregate materializzate con una
  • clausola GROUP BY non possono selezionare da una tabella organizzata per indice. 5.3.8.5 Restrizioni su Fast Refresh su Viste Materializzate con Solo Joins

Le 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 RefreshNon possono avere«.
  • clausole o aggregati. BY non possono selezionare da una tabella organizzata per indice. Gli Rowid di tutte le tabelle nella
  • lista devono apparire nella DA lista della query. SELECT I 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 DA I registri delle viste materializzate devono esistere con rowid per tutte le tabelle di base nella
  • istruzione. SELECT Inoltre, 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 SELECT 5.3.8.6 Restrizioni su Fast Refresh su Viste Materializzate con Aggregati

Le query definite per le viste materializzate con aggregati o join hanno le seguenti restrizioni sul fast refresh:

Il fast refresh è supportato per entrambe le

viste materializzate per RICHIESTA, tuttavia si applicano le seguenti restrizioni: ACCESO COMMIT and ACCESO Tutte 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 NUOVO and Specificare la SEQUENZA VALUES.
    • clausola se ci si aspetta che la tabella abbia una combinazione di inserimenti/caricamenti diretti, cancellazioni e aggiornamenti. Solo SOMMA

  • COUNT STDDEV, VARIANZA, AVG, MAX, sono supportati per il refresh veloce., MIN and COUNT(*) 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)) or AVG(x)+ AVG(x) non sono consentite.
  • Per ogni aggregato come AVG(expr), deve essere presente il corrispondente COUNT(expr) . Oracle consiglia di specificare SUM(expr) .
  • Se VARIANCE(expr) or STDDEV(expr) è specificato, COUNT(expr) and SUM(expr) deve essere specificato. Oracle consiglia di specificare SUM(expr *expr) .
  • Il SELECT la 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 SELECT l'elenco deve contenere tutte BY non 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 CHAR tipo 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 MIN or COUNT(*) aggregati
    • Le viste materializzate che hanno SUM(expr) ma non COUNT(expr)
    • Le viste materializzate senza COUNT(*)

    Tale vista materializzata è chiamata vista materializzata solo per inserimenti.

  • Una vista materializzata con COUNT(*) or MIN è aggiornabile rapidamente dopo cancellazioni o istruzioni DML miste se non ha un DOVE clausola.
    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 DA clausola possono essere aggiornate rapidamente, a condizione che le viste possano essere completamente unite. Per informazioni su quali viste si uniranno, vedere Oracle Database SQL Language Reference.
  • Se non ci sono join esterni, puoi avere selezioni e join arbitrari nella DOVE clausola.
  • 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 SELECT l'elenco deve contenere un distintivo di raggruppamento che può essere o un GROUPING_ID funzione su tutte BY non possono selezionare da una tabella organizzata per indice. le espressioni o funzioni GROUPING, una per ogni espressione. Ad esempio, se la BY non possono selezionare da una tabella organizzata per indice. clausola della vista materializzata è « BY non possono selezionare da una tabella organizzata per indice. CUBE(a, b)BY non 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. E non dovrebbe risultare in raggruppamenti duplicati. Ad esempio, «GROUP BY a, ROLLUP(a, b)
    • BY non 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 UNIONE ALL FAST se le seguenti condizioni sono soddisfatte: La query definente deve avere l'operatore al livello superiore.

  • l'operatore non può essere incorporato all'interno di una sottoquery, con una eccezione: Il UNIONE ALL può trovarsi in una sottoquery nella

    Il UNIONE ALL clausola a condizione che la query definente sia della forma UNIONE ALL SELECT * FROM DA (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; UNIONE ALLNota che la vista

    view_with_unionall
    

    soddisfa i requisiti per un aggiornamento rapido. Ogni blocco di query nella query 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. UNIONE ALL ntext

    filestream
    Nota che l'Oracle Database consente anche il caso speciale di una vista materializzata su una singola tabella con join a condizione che NUOVO la colonna sia stata inclusa nella SELECT lista e nel log della vista materializzata. Questo è mostrato nella query di definizione della vista Ogni blocco di query nella.

  • Il SELECT la lista di ogni query deve includere un UNIONE ALL marcatore, e la UNIONE ALL colonna deve avere un valore numerico o di stringa costante distinto in ciascun UNIONE ALL ramo. Inoltre, la colonna marcatore deve apparire nella stessa posizione ordinale nella SELECT lista di ogni blocco di query. Vedi «Marcatore UNION ALL e Riscrittura della Query» per ulteriori informazioni riguardo ai UNIONE ALL mark. 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 UNIONE ALLo tabelle remote sono utilizzate. UNIONE ALL Il 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. UNIONE ALL.

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 INDEX Quando 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à
    Note

    PRECISO = 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, OPENXMLOUTER join ()
    SINISTRO DESTRATabella derivata (definita specificando una, dichiarazione nella[START WITH …] CONNECT BY FULL)

    clausola) SELECT Self-join DA Specificando colonne utilizzando
    SELECT *
    SELECT <nome_tabella>.* STDEV or STDEVP

    DISTINCT
    VAR, VARP, Espressione di tabella comune (CTE), ntext[START WITH …] CONNECT BY AVG
    filestream

    float1, text, ntext, image, XML[START WITH …] CONNECT BY filestream colonne
    Sottoquery
    OVER clausola, che include funzioni di finestra di ranking o aggregazione

    Predicati di full-text (CONTENERE, FREETEXT)
    STDDEV funzione che si riferisce a un'espressione nullable
    ORDER BY

    funzione di aggregazione definita dall'utente CLR
    TOP
    CUBE, ROLLUP[START WITH …] CONNECT BY SET DI GRUPPI operatori

    MIN, COUNT(*)
    UNIONE, ECCETTO[START WITH …] CONNECT BY INTERSEZIONE operatori
    TABLESAMPLE

    Variabili di tabella
    OUTER APPLY or CROSS APPLY
    PIVOT, UNPIVOT

    Set di colonne sparse
    Funzioni a valore di tabella inline (TVF) o multichiamata (MSTVF)
    OFFSET

    CHECKSUM_AGG

    1 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 contenere COUNT_BIG(*) e non deve contenere clausola HAVING con una sottoselezione.. 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 una GROUP BY. clausola, la chiave dell'indice cluster unico può fare riferimento solo alle colonne specificate nella GROUP 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, OPENXMLOUTER join ()
SINISTRO DESTRATabella derivata (definita specificando una, dichiarazione nella[START WITH …] CONNECT BY FULL)

clausola) SELECT Self-join DA Specificando colonne utilizzando
SELECT *
SELECT <nome_tabella>.* STDEV or STDEVP

DISTINCT
VAR, VARP, Espressione di tabella comune (CTE), ntext[START WITH …] CONNECT BY AVG
filestream

float1, text, ntext, image, XML[START WITH …] CONNECT BY filestream colonne
Sottoquery
OVER clausola, che include funzioni di finestra di ranking o aggregazione

Predicati di full-text (CONTENERE, FREETEXT)
STDDEV funzione che si riferisce a un'espressione nullable
ORDER BY

funzione di aggregazione definita dall'utente CLR
TOP
CUBE, ROLLUP[START WITH …] CONNECT BY SET DI GRUPPI operatori

MIN, COUNT(*)
UNIONE, ECCETTO[START WITH …] CONNECT BY INTERSEZIONE operatori
TABLESAMPLE

Variabili di tabella
OUTER APPLY or CROSS APPLY
PIVOT, UNPIVOT

Set 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 codice sorgente. 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 Sistemi ERP, 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 piattaforma e PostgreSQL, abilitato 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

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