Echilibrarea scrierii și citirii în baza de date

Echilibrarea scrierii și citirii în baza de date
În articolul anterior pe care l-ați citit Am descris conceptul și implementarea unei baze de date bazate pe funcții, nu pe tabele și câmpuri, așa cum se întâmplă în bazele de date relaționale. Aceasta a inclus numeroase exemple care demonstrează avantajele unei astfel de abordări față de cea clasică. Mulți au considerat aceste exemple insuficient de convingătoare.

În acest articol, voi arăta cum o astfel de concepție permite echilibrarea rapidă și eficientă a scrierilor și citirilor într-o bază de date fără a modifica logica de funcționare. Funcționalități similare au fost încercate să fie implementate în sistemele DBMS comerciale moderne (în special, Oracle și Microsoft SQL Server). La finalul articolului, voi arăta ce au realizat ei, fără a fi tocmai impresionant.

Descriere

Așa cum am procedat anterior, pentru o mai bună înțelegere, voi începe descrierea cu exemple. Să presupunem că trebuie să implementăm o logică care va returna o listă cu departamentele și numărul de angajați din acestea, precum și salariul lor total.

Într-o bază de date funcțională, aceasta ar arăta astfel:

CLASS Departament ‘Departament’;
name ‘Denumire’ = DATA STRING[100] (Departament);

CLASS Employee 'Angajat';
department 'Departament' = DATA Department (Employee);
salary 'Salariu' = DATA NUMERIC[10,2] (Employee);

countEmployees 'Număr angajați' (Department d) = 
    GROUP SUM 1 IF department(Employee e) = d;
salarySum 'Salariu Total' (Department d) = 
    GROUP SUM salary(Employee e) IF department(e) = d;

SELECT name(Department d), countEmployees(d), salarySum(d);

Complexitatea executării acestei interogări în orice DBMS va fi echivalentă cu O(numărul angajaților), deoarece pentru această calculare este necesar să se scaneze întreaga tabelă de angajați și apoi să se grupeze în funcție de departament. De asemenea, va exista o mică (considerând că numărul angajaților este mult mai mare decât cel al departamentelor) adăugare în funcție de planul ales. O(log numărul angajaților) sau O(numărul departamentelor) pentru grupare și altele.

Este clar că costurile de execuție pot varia în funcție de DBMS, dar complexitatea nu se va schimba.

În implementarea propusă, sistemul de gestionare a bazelor de date funcționale va forma o sub-interogare, care va calcula valorile necesare pe departament, iar apoi va face un JOIN cu tabela departamentelor pentru a obține numele. Totuși, la declararea fiecărei funcții există posibilitatea de a specifica un marcaj special MATERIALIZED. Sistemul va crea automat câmpul corespunzător pentru fiecare astfel de funcție. Atunci când valoarea funcției se schimbă, valoarea câmpului va fi de asemenea modificată în aceeași tranzacție. La apelarea acestei funcții, se va accesa câmpul deja calculat.

În special, dacă se setează MATERIALIZED pentru funcții countEmployees și salarySum, se vor adăuga două câmpuri în tabela cu lista departamentelor, în care vor fi stocate numărul de angajați și totalul salariilor acestora. La orice modificare a angajaților, a salariilor acestora sau a apartenenței la departamente, sistemul va actualiza automat valorile acestor câmpuri. Interogarea menționată mai sus va accesa direct aceste câmpuri și va fi executată în O(numărul departamentelor).

Ce restricții există? Doar una: o astfel de funcție trebuie să aibă un număr finit de valori de intrare pentru care valoarea ei este definită. Altfel, nu va fi posibil să se construiască o tabelă care să păstreze toate valorile sale, deoarece nu poate exista o tabelă cu un număr infinit de rânduri.

Exemplu:

employeesCount ‘Numărul de angajați cu salariul > N’ (Department d, NUMERIC[10,2] N) = 
    GROUP SUM salary(Employee e) IF department(e) = d AND salary(e) > N;

Această funcție este definită pentru un număr infinit de valori ale numărului N (de exemplu, se potrivesc orice valori negative). Prin urmare, nu poate avea setat MATERIALIZED. Astfel, aceasta este o restricție logică, nu tehnică (adică nu din cauza că nu am reușit să o implementăm). În rest, nu există alte restricții. Se pot folosi grupări, sortări, AND și OR, PARTITION, recursiuni etc.

De exemplu, în exercițiul 2.2 din articolul anterior se poate seta MATERIALIZED pentru ambele funcții:

cumpărat 'Купил' (Client c, Produs p, NUMĂR întreg y) = 
    GRUP SUMA sum(Detaliu d) ÎN CAZ 
        client(ordonare(d)) = c ȘI 
        produs(d) = p ȘI 
        extrageAnul(data(ordonare(d))) = y MATERIAZAT;
rating 'Рейтинг' (Client c, Produs p, NUMĂR întreg y) = 
    PARTIȚIE SUMA 1 ORDINE DESC cumpărat(c, p, y), p ÎN FUNCȚIE DE c, y MATERIAZAT;
SELECTEAZĂ contactName(Client c), nume(Produs p) UNDE rating(c, p, 1997) < 3;

Sistemul va crea automat o tabelă cu cheile tipurilor Client, Product și INTEGER, va adăuga două câmpuri în aceasta și va actualiza valorile acestora la orice modificări. La apelurile ulterioare către aceste funcții, nu va avea loc calculul acestora, ci se vor citi valorile din câmpurile corespunzătoare.

Prin acest mecanism, se poate, de exemplu, renunța la recursiuni (CTE) în interogări. În special, să considerăm grupurile care formează un arbore prin relația child/parent (fiecare grup are o referință la părintele său):

parent = DATA Group (Group);

În baza de date funcțională, logica recursiilor poate fi definită în modul următor:

nivel (copil Grup, părinte Grup) = RECURNȚĂ 1l DACĂ copil ESTE Grup ȘI părinte == copil
                                                             PASUL 2l DACĂ părinte == părinte($parent);
estePărinte (copil Grup, părinte Grup) = ADEVĂR DACĂ nivel(copil, părinte) MATERIALIZAT;

Deoarece pentru funcția isParent este setat MATERIALIZED, va fi creată o tabelă cu două chei (grupuri), în care câmpul isParent va fi adevărat doar dacă prima cheie este descendantă a celei de-a doua. Numărul de înregistrări în această tabelă va fi egal cu numărul de grupuri înmulțit cu adâncimea medie a arborelui. Dacă este necesar, de exemplu, să se calculeze numărul de descendenți ai unui anumit grup, se poate apela la această funcție:

childrenCount (Group g) = GROUP SUM 1 IF isParent(Group child, g);

Nu va exista niciun CTE în interogarea SQL. În schimb, va fi un simplu GROUP BY.

Prin acest mecanism, se poate realiza cu ușurință și denormalizarea bazei de date, dacă este nevoie:

CLASS Order 'Comandă';
date 'Data' = DATA DATE (Comandă);

CLASS OrderDetail 'Detaliu comandă';
order 'Comandă' = DATA Order (OrderDetail);
date 'Data' (OrderDetail d) = date(order(d)) MATERIALIZED INDEXED;

Când se accesează funcția date pentru detaliul comenzii, se va efectua citirea din tabelul cu rândurile comenzilor pe câmpul pentru care există un index. Atunci când data comenzii se schimbă, sistemul va recalcula automat data denormalizată în rând.

Avantajele

De ce este necesar tot acest mecanism? În sistemele de gestionare a bazelor de date clasice, fără a rescrie interogările, dezvoltatorul sau DBA poate doar să modifice indecșii, să definească statistici și să sugereze planificatorului de interogări cum să le execute (întrucât HINT-urile există doar în sistemele comerciale de gestionare a bazelor de date). Indiferent cât de mult s-ar strădui, nu vor putea executa prima interogare din articol în O (numărul de departamente) fără a modifica interogările și adăugând trigger-e. În schema propusă, în etapa de dezvoltare, nu trebuie să te gândești la structura stocării datelor și la ce agregări să folosești. Toate acestea pot fi modificate cu ușurință pe parcurs, direct în exploatare.

În practică, aceasta arată în felul următor. Anumiți oameni dezvoltă direct logica pe baza cerințelor stabilite. Ei nu se pricep nici la algoritmi și complexitatea acestora, nici la planurile de execuție, nici la tipurile de join-uri, nici la vreo altă componentă tehnică. Acești oameni sunt mai degrabă analiști de business decât dezvoltatori. Apoi, totul este supus testării sau exploatării. Se activează logarea cererilor lungi. Când se detectează o cerere îndelungată, decizia de a activa MATERIALIZED pe o anumită funcție intermediară este luată de oameni diferiți (mai tehnici — de fapt, DBA). Astfel, scrierea este încetinită puțin (deoarece este necesară actualizarea unui câmp suplimentar în tranzacție). Totuși, nu doar această cerere se accelerează semnificativ, ci și toate celelalte care utilizează această funcție. În acest context, decizia despre ce funcție anume să fie materializată este relativ simplă. Două parametrii de bază: numărul valorilor de intrare posibile (exact atâtea înregistrări vor fi în tabelul corespunzător) și cât de des este folosită aceasta în alte funcții.

Analogi

În sistemele de baze de date comerciale moderne există mecanisme similare: MATERIALIZED VIEW cu FAST REFRESH (Oracle) și INDEXED VIEW (Microsoft SQL Server). În PostgreSQL, MATERIALIZED VIEW nu poate fi actualizată în cadrul unei tranzacții, ci numai la cerere (iar asta cu anumite restricții severe), deci nu o luăm în considerare. Însă acestea au câteva probleme care limitează semnificativ utilizarea lor.

În primul rând, poți activa materializarea doar dacă ai creat deja un VIEW obișnuit. Altfel, va trebui să rescrii celelalte cereri pentru a face referire la noul view creat, astfel încât să utilizezi această materializare. Sau poți lăsa totul așa cum este, dar va fi, cel puțin, ineficient, dacă există anumite date deja calculate, dar multe cereri nu le folosesc întotdeauna, ci le calculează din nou.

În al doilea rând, acestea vin cu un număr mare de restricții:

Oracle

5.3.8.4 Restricții generale asupra Fast Refresh

Interogarea care definește view-ul materializat este restricționată după cum urmează:

  • View-ul materializat nu trebuie să conțină referințe la expresii non-repetitive, cum ar fi SYSDATE și ROWNUM.
  • View-ul materializat nu trebuie să conțină referințe la RAW sau LONG RAW tipuri de date.
  • Nu poate conține o SELECT subinterogare în listă.
  • Nu poate conține funcții analitice (de exemplu, RANK) în clauza SELECT .
  • Nu poate face referire la un tabel pe care este definit un XMLIndex index.
  • Nu poate conține o MODEL .
  • Nu poate conține o clauza HAVING cu o subinterogare. Nu poate conține interogări imbricate care au
  • ANY ALL, , sauNOT EXISTS [START WITH …] CONNECT BY.
  • Nu poate conține o [START WITH …] CONNECT BY .
  • Nu poate conține multiple tabele detaliu localizate în diferite site-uri.
  • DA COMMIT materialized views nu pot avea tabele detaliu remote.
  • Materialized views încrucișate trebuie să aibă un join sau un agregat.
  • Materialized join views și materialized aggregate views cu un GRUP DE clauza nu poate selecta dintr-un tabel organizat prin index.

5.3.8.5 Restricții privind refresh-ul rapid pentru materialized views cu doar joins

Definirea interogărilor pentru materialized views cu doar joins și fără agregate are următoarele restricții privind refresh-ul rapid:

  • Toate restricțiile din „Restricții Generale privind Refresh-ul Rapid«.
  • Ele nu pot avea GRUP DE clauze sau agregate.
  • Rowids ale tuturor tabelelor din FROM listă trebuie să apară în SELECT lista interogării.
  • Jurnalele de vizualizare materializată trebuie să existe cu rowids pentru toate tabelele de bază din FROM lista interogării.
  • Nu poți crea o materialized view refreshabilă rapid din multiple tabele cu joins simple care includ o coloană de tip obiect în SELECT declarație.

De asemenea, metoda de refresh aleasă nu va fi optim eficientă dacă:

  • Interogarea de definiție folosește un outer join care se comportă ca un inner join. Dacă interogarea de definiție conține un astfel de join, ia în considerare rescrierea interogării de definiție pentru a conține un inner join.
  • The SELECT lista materialized view conține expresii pe coloane din mai multe tabele.

5.3.8.6 Restricții privind refresh-ul rapid pentru materialized views cu agregate

Definirea interogărilor pentru materialized views cu agregate sau joins are următoarele restricții privind refresh-ul rapid:

Refresh-ul rapid este suportat pentru ambele DA COMMIT și DA CERERE materialized views, totuși următoarele restricții se aplică:

  • Toate tabelele din materialized view trebuie să aibă jurnale de vizualizare materializată, iar jurnalele de vizualizare materializată trebuie să:
    • Conțină toate coloanele din tabelul referit în materialized view.
    • Specifice cu ROWID și INCLUD NOI VALUES.
    • Specificați SEQUENCE clauza dacă tabelul este așteptat să aibă un amestec de inserții/încărcări directe, ștergeri și actualizări.

  • Numai SUM, COUNT, AVG, STDDEV, VARIANCE, MIN și MAX sunt acceptate pentru refresh rapid.
  • COUNT(*) trebuie specificat.
  • Funcțiile de agregare trebuie să apară doar ca partea exterioară a expresiei. Adică, agregatele, cum ar fi AVG(AVG(x)) sau AVG(x)+ AVG(x) nu sunt permise.
  • Pentru fiecare agregat, cum ar fi AVG(expr), contul corespunzător COUNT(expr) trebuie să fie prezent. Oracle recomandă ca SUM(expr) să fie specificat.
  • Dacă VARIANCE(expr) sau STDDEV(expr) este specificat, COUNT(expr) și SUM(expr) trebuie să fie specificat. Oracle recomandă ca SUM(expr *expr) să fie specificat.
  • The SELECT coloana în interogarea de definiție nu poate fi o expresie complexă cu coloane din mai multe tabele de bază. O posibilă soluție pentru aceasta este utilizarea unei materialized view încrucișate.
  • The SELECT lista trebuie să conțină toate GRUP DE coloanele.
  • Materialized view nu se bazează pe unul sau mai multe tabele remote.
  • Dacă utilizați un CHAR tip de date în coloanele de filtrare ale unui jurnal de vizualizare materializată, seturile de caractere ale site-ului principal și cele ale materialized view trebuie să fie identice.
  • Dacă materialized view are una dintre următoarele, atunci refresh-ul rapid este suportat doar pentru inserții DML convenționale și încărcări directe.
    • Materialized views cu MIN sau MAX agregate
    • Materialized views care au SUM(expr) dar fără COUNT(expr)
    • Materialized views fără COUNT(*)

    O astfel de materialized view se numește materialized view doar pentru inserții.

  • O materialized view cu MAX sau MIN este refreshabil rapid după ștergeri sau instrucțiuni DML mixte dacă nu are un WHERE .
    Max/Min refresh rapid după ștergeri sau instrucțiuni DML mixte nu are aceeași comportare ca și cazul doar pentru inserții. Se șterge și se recalculează valorile max/min pentru grupurile afectate. Trebuie să fii conștient de impactul său asupra performanței.
  • Materialized views cu vizualizări numite sau subinterogări în FROM clauză pot fi refreshate rapid, cu condiția ca vizualizările să poată fi complet combinate. Pentru informații despre vizualizările care se vor combina, vezi Referința limbajului SQL Oracle Database.
  • Dacă nu există outer joins, puteți avea selecții și joins arbitrare în WHERE .
  • Materialized aggregate views cu outer joins sunt refreshabile rapid după DML convențional și încărcări directe, cu condiția ca doar tabelul exterior să fi fost modificat. De asemenea, constrângerile unice trebuie să existe pe coloanele de join ale tabelului inner join. Dacă există outer joins, toate joins trebuie să fie conectate de ȘIs și trebuie să folosească egalitatea (=) operator.
  • Pentru vizualizările materializate cu CUBE, ROLLUP, seturile de grupare sau concatenarea acestora, se aplică următoarele restricții:
    • The SELECT lista trebuie să conțină un distingător de grupare care poate fi fie un GROUPING_ID funcție aplicată pe toate GRUP DE expresiile sau GROUPING funcții pentru fiecare GRUP DE expresie. De exemplu, dacă GRUP DE clauza vizualizării materializate este „GRUP DE CUBE(a, b)"atunci lista ar trebui să conțină fie „ SELECT GROUPING_ID(a, b)"sau „GROUPING(a)GROUPING(b) ȘI ” pentru ca vizualizarea materializată să fie refreshabilă rapid.nu ar trebui să conducă la grupări duplicate. De exemplu, „
    • GRUP DE GROUP BY a, ROLLUP(a, b)"nu este refreshabil rapid deoarece duce la grupări duplicate „(a), (a, b) și (a)5.3.8.7 Restricții asupra Refreshului Rapid pentru Vizualizările Materializate cu UNION ALL«.

Vizualizările materializate cu operatorul de seturi sprijină

REFRESH UNION , sau RAPID opțiunea dacă sunt îndeplinite următoarele condiții: Interogarea de definire trebuie să aibă operatorul la nivelul de bază. operatorul nu poate fi încorporat într-o subinterogare, cu o excepție: „

  • poate fi într-o subinterogare în clauza UNION , sau cu condiția ca interogarea de definire să fie de forma

    The UNION , sau SELECT * FROM UNION , sau (vizualizare sau subinterogare cu FROM ) după cum se arată în următorul exemplu: 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; Observați că vizualizarea UNION , sauview_with_unionall

    satisfacă cerințele pentru refresh rapid.
    

    Fiecare bloc de interogare din interogarea trebuie să respecte cerințele unei vizualizări materializate refreshabile rapid cu agregate sau a unei vizualizări materializate refreshabile rapid cu join-uri. Jurnalele de vizualizare materializată corespunzătoare trebuie să fie create pe tabele, așa cum este necesar pentru tipul corespunzător de vizualizare materializată refreshabilă rapid.

  • Observați că Oracle Database permite, de asemenea, cazul special al unei vizualizări materializate a unei singure tabele cu join-uri doar cu condiția ca UNION , sau coloana să fi fost inclusă în

    listă și în jurnalul vizualizării materializate. Acest lucru este prezentat în interogarea de definire a vizualizării
    lista fiecărei interogări trebuie să includă un ROWID marcator, iar SELECT coloana trebuie să aibă o valoare constantă numerică sau de șir distinctă în fiecare trebuie să respecte cerințele unei vizualizări materializate refreshabile rapid cu agregate sau a unei vizualizări materializate refreshabile rapid cu join-uri..

  • The SELECT ramură. În plus, coloana de marcator trebuie să apară în aceeași poziție ordinală în UNION , sau lista fiecărui bloc de interogare. Consultați „ UNION , sau UNION ALL Marker and Query Rewrite UNION , sau ” pentru mai multe informații referitoare la SELECT marcatori.Unele caracteristici, cum ar fi join-urile externe, interogările agregate materializate de tip insert-only și tabelele externe nu sunt acceptate pentru vizualizările materializate cu. Rețineți, totuși, că vizualizările materializate utilizate în replicare, care nu conțin join-uri sau agregate, pot fi refreshate rapid când UNION , sau sau tabele externe sunt utilizate.
  • Parametrul inițial de compatibilitate trebuie să fie setat la 9.2.0 sau mai mare pentru a crea o vizualizare materializată refreshabilă rapid cu UNION , sauNu vreau să ofensez fanii Oracle, dar având în vedere lista lor de restricții, se dă impresia că acest mecanism a fost scris nu în cazul general, folosind un fel de model, ci de mii de indieni, unde fiecăruia i s-a dat liber să scrie ramura sa, iar fiecare a făcut ceea ce a putut. Folosirea acestui mecanism pentru logica reală este ca și cum ai merge pe un câmp minat. În orice moment poți da peste o mină, întâlnind una dintre restricțiile neprevăzute. Cum funcționează — aceasta este o întrebare separată, dar se află în afara acestui articol. UNION , sau Microsoft SQL Server
  • Cerințe suplimentare UNION , sau.

În plus față de opțiunile SET și cerințele funcțiilor deterministe, următoarele cerințe trebuie să fie îndeplinite:

Microsoft SQL Server

Cerințe suplimentare

Pe lângă opțiunile SET și cerințele pentru funcții deterministe, trebuie îndeplinite următoarele cerințe:

  • Utilizatorul care execută CREAȚI INDEX trebuie să fie proprietarul vederii.
  • Atunci când creați indexul, opțiunea IGNORE_DUP_KEY trebuie să fie setată pe OFF (setarea implicită).
  • Tabelele trebuie să fie referite prin nume în două părți, schema.numele_tabelei în definiția vederii.
  • Funcțiile definite de utilizator referite în vedere trebuie să fie create folosind WITH SCHEMABINDING opțiunea.
  • Orice funcții definite de utilizator referite în vedere trebuie să fie menționate prin nume în două părți, <schema>.<function>.
  • Proprietatea de acces la date a unei funcții definite de utilizator trebuie să fie NU SQL, iar proprietatea de acces extern trebuie să fie NU.
  • Funcțiile din timpul execuției comune (CLR) pot apărea în lista de selecție a vederii, dar nu pot face parte din definiția cheii indexului clusterizat. Funcțiile CLR nu pot apărea în clauza WHERE a vederii sau în clauza ON a unei operațiuni JOIN în vedere.
  • Funcțiile CLR și metodele tipurilor definite de utilizator CLR utilizate în definiția vederii trebuie să aibă proprietățile setate conform următoarei tabele.

    Proprietate
    Note

    DETERMINISTICE = ADEVĂRAT
    Trebuie declărate explicit ca atribut al metodei Microsoft .NET Framework.

    PRECIS = ADEVĂRAT
    Trebuie declărate explicit ca atribut al metodei .NET Framework.

    ACCES LA DATE = NU SQL
    Stabilit prin setarea atributului DataAccess la DataAccessKind.None și atributul SystemDataAccess la SystemDataAccessKind.None.

    ACCES EXTERN = NU
    Această proprietate are în mod implicit valoarea NU pentru rutinele CLR.

  • Vederea trebuie să fie creată folosind WITH SCHEMABINDING opțiunea.
  • Vederea trebuie să facă referire doar la tabele de bază care se află în aceeași bază de date ca și vederea. Vederea nu poate face referire la alte vederi.
  • Declarația SELECT din definiția vederii nu trebuie să conțină următoarele elemente Transact-SQL:

    COUNT
    Funcții ROWSET (OPENDATASOURCE, OPENQUERY, OPENROWSET, ȘI OPENXML)
    JOINURI EXTERNE ( STÂNGADREAPTA, Tabel derivat (definit prin specificarea unuiNOT FULL)

    declarație în clauza SELECT ) Auto-joinuri FROM Specificați coloanele folosind
    SELECT *
    SELECT .* STDEV sau STDEVP

    DISTINCT
    VAR, VARP, Expresie comună a tabelului (CTE), ntextNOT AVG
    filestream

    float1, text, coloane, imagine, XMLNOT Subinterogare PESTE
    clauză, care include funcții de fereastră de clasificare sau agregare
    Predicați de text complet ( CONTAINS

    FREETEXTfuncție care face referire la o expresie nullable, funcție agregată definită de utilizator CLR)
    SUM TOP
    ORDER BY

    SETURI DE GRUPARE
    operatori
    CUBE, ROLLUPNOT EXCEPT INTERSECT

    MIN, MAX
    UNION, TABEL SAMPLEDNOT Variabile de tabel INTERSECT
    OUTER APPLY

    CROSS APPLY
    PIVOT sau UNPIVOT
    Seturi de coloane sparse, Funcții definite de utilizator de tip tabel (TVF) inline sau funcții cu mai multe instrucțiuni (MSTVF)

    CHECKSUM_AGG
    1 Vederea indexată poate conține
    OFFSET

    coloane; totuși, astfel de coloane nu pot fi incluse în cheia indexului clusterizat.

    GROUP BY float este prezentă, definiția VEDERII trebuie să conțină

  • Dacă COUNT_BIG(*) și nu trebuie să conțină . Aceste restricții sunt aplicabile doar definiției vederii indexate. O interogare poate utiliza o vedere indexată în planul său de execuție chiar dacă nu respectă aceste clauza HAVING cu o subinterogare.restricții. COUNT_BIG(*) Dacă definiția vederii conține o COUNT_BIG(*) clauză, cheia indexului clusterizat unic poate face referire doar la coloanele specificate în
  • Aici se vede că indienii nu au fost atrași, deoarece au decis să facă după schema 'vom face puțin, dar bine'. Adică, au minat mai multe pe câmp, dar plasarea lor este mai transparentă. Cel mai mult mă supără această restricție: COUNT_BIG(*) În terminologia noastră, aceasta înseamnă că funcția nu poate apela o altă funcție materializată. Acest lucru taie toată ideologia la rădăcină. COUNT_BIG(*) .

De asemenea, această restricție (și mai departe în text) reduce foarte mult opțiunile de utilizare:

Vederea trebuie să facă referire doar la tabele de bază care se află în aceeași bază de date ca și vederea. Vederea nu poate face referire la alte vederi.

Outer Joins, UNION, ORDER BY și altele sunt interzise. Poate că ar fi fost mai simplu să se indice ce poate fi folosit, decât ceea ce nu poate. Lista ar fi fost probabil mult mai mică.
De asemenea, această restricție (și continuarea textului) reduce semnificativ variantele de utilizare:

Declarația SELECT din definiția vederii nu trebuie să conțină următoarele elemente Transact-SQL:

COUNT
Funcții ROWSET (OPENDATASOURCE, OPENQUERY, OPENROWSET, ȘI OPENXML)
JOINURI EXTERNE ( STÂNGADREAPTA, Tabel derivat (definit prin specificarea unuiNOT FULL)

declarație în clauza SELECT ) Auto-joinuri FROM Specificați coloanele folosind
SELECT *
SELECT .* STDEV sau STDEVP

DISTINCT
VAR, VARP, Expresie comună a tabelului (CTE), ntextNOT AVG
filestream

float1, text, coloane, imagine, XMLNOT Subinterogare PESTE
clauză, care include funcții de fereastră de clasificare sau agregare
Predicați de text complet ( CONTAINS

FREETEXTfuncție care face referire la o expresie nullable, funcție agregată definită de utilizator CLR)
SUM TOP
ORDER BY

SETURI DE GRUPARE
operatori
CUBE, ROLLUPNOT EXCEPT INTERSECT

MIN, MAX
UNION, TABEL SAMPLEDNOT Variabile de tabel INTERSECT
OUTER APPLY

CROSS APPLY
PIVOT sau UNPIVOT
Seturi de coloane sparse, Funcții definite de utilizator de tip tabel (TVF) inline sau funcții cu mai multe instrucțiuni (MSTVF)

CHECKSUM_AGG
1 Vederea indexată poate conține
OFFSET

coloane; totuși, astfel de coloane nu pot fi incluse în cheia indexului clusterizat.

Îmbinările OUTER, UNION, ORDER BY și altele sunt interzise. Poate că a fost mai simplu să se precizeze ce este permis, decât ceea ce nu este. Lista ar fi fost probabil mult mai scurtă.

În concluzie: un set enorm de restricții în fiecare (menționez comercială) SGBD vs. niciuna (cu excepția uneia logice, nu tehnice) în tehnologia LGPL. Cu toate acestea, merită să menționăm că implementarea acestui mecanism în logica relațională este oarecum mai complicată decât în cea funcțională descrisă.

Implementarea

Cum funcționează? Ca „mașină virtuală” este folosit PostgreSQL. În interior există un algoritm complex care se ocupă cu construirea interogărilor. Iată codul sursă. Și acolo nu este vorba doar de un set mare de euristici cu o mulțime de if-uri. Așadar, dacă aveți câteva luni pentru a învăța, puteți încerca să înțelegeți arhitectura.

Funcționează acesta eficient? Suficient de eficient. Din păcate, este greu de dovedit. Pot spune doar că, dacă luăm în considerare mii de interogări care există în aplicații mari, atunci, în medie, sunt mai eficiente decât cele scrise de un dezvoltator bun. Un programator SQL excelent poate scrie orice interogare mai eficient, dar pentru o mie de interogări nu va avea pur și simplu nicio motivație sau timp să facă acest lucru. Singurul lucru pe care îl pot aduce acum ca dovadă a eficienței este că pe baza platformei construite pe acest SGBD funcționează mai multe proiecte sisteme ERP, în care există mii de funcții MATERIALIZED diferite, cu mii de utilizatori și baze de date terabit cu sute de milioane de înregistrări, funcționând pe un server normal cu două procesoare. Totuși, oricine este interesat poate verifica/opri eficiența, descărcând platforma și PostgreSQL, activând logarea interogărilor SQL și încercând să modifice acolo logica și datele.

În articolele următoare, voi vorbi, de asemenea, despre cum se pot impune restricții asupra funcțiilor, lucrul cu sesiunile de modificări și multe altele.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster