
În articolul anterior 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șiROWNUM.- View-ul materializat nu trebuie să conțină referințe la
RAWsauLONGRAWtipuri de date.- Nu poate conține o
SELECTsubinterogare în listă.- Nu poate conține funcții analitice (de exemplu,
RANK) în clauzaSELECT.- Nu poate face referire la un tabel pe care este definit un
XMLIndexindex.- 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,, sauNOTEXISTS[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.
DACOMMITmaterialized 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
GRUPDEclauza 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 „«.
- Ele nu pot avea
GRUPDEclauze sau agregate.- Rowids ale tuturor tabelelor din
FROMlistă trebuie să apară înSELECTlista interogării.- Jurnalele de vizualizare materializată trebuie să existe cu rowids pentru toate tabelele de bază din
FROMlista 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
SELECTdeclaraț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
SELECTlista 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:
- Toate restricțiile din „«.
Refresh-ul rapid este suportat pentru ambele
DACOMMITșiDACEREREmaterialized 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șiINCLUDNOIVALUES.- Specificați
SEQUENCEclauza 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șiMAXsunt 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))sauAVG(x)+AVG(x)nu sunt permise.- Pentru fiecare agregat, cum ar fi
AVG(expr), contul corespunzătorCOUNT(expr)trebuie să fie prezent. Oracle recomandă caSUM(expr)să fie specificat.- Dacă
VARIANCE(expr)sauSTDDEV(expr) este specificat,COUNT(expr)șiSUM(expr)trebuie să fie specificat. Oracle recomandă caSUM(expr *expr)să fie specificat.- The
SELECTcoloana î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
SELECTlista trebuie să conțină toateGRUPDEcoloanele.- Materialized view nu se bazează pe unul sau mai multe tabele remote.
- Dacă utilizați un
CHARtip 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
MINsauMAXagregate- 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
MAXsauMINeste refreshabil rapid după ștergeri sau instrucțiuni DML mixte dacă nu are unWHERE.
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
FROMclauză 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 .- 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
SELECTlista trebuie să conțină un distingător de grupare care poate fi fie unGROUPING_IDfuncție aplicată pe toateGRUPDEexpresiile sauGROUPINGfuncții pentru fiecareGRUPDEexpresie. De exemplu, dacăGRUPDEclauza vizualizării materializate este „GRUPDECUBE(a, b)"atunci lista ar trebui să conțină fie „SELECTGROUPING_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, „GRUPDEGROUP 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, sauRAPIDopț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, saucu condiția ca interogarea de definire să fie de formaThe
UNION, sauSELECT * FROMUNION, sau(vizualizare sau subinterogare cuFROM) 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ă vizualizareaUNION, sauview_with_unionallsatisfacă 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, saucoloana să fi fost inclusă înlistă ș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ă unROWIDmarcator, iarSELECTcoloana trebuie să aibă o valoare constantă numerică sau de șir distinctă în fiecaretrebuie să respecte cerințele unei vizualizări materializate refreshabile rapid cu agregate sau a unei vizualizări materializate refreshabile rapid cu join-uri..- The
SELECTramură. În plus, coloana de marcator trebuie să apară în aceeași poziție ordinală înUNION, saulista fiecărui bloc de interogare. Consultați „UNION, sauUNION ALL Marker and Query RewriteUNION, sau” pentru mai multe informații referitoare laSELECTmarcatori.. 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ândUNION, sausau 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, sauMicrosoft 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 INDEXtrebuie să fie proprietarul vederii.- Atunci când creați indexul, opțiunea
IGNORE_DUP_KEYtrebuie 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 SCHEMABINDINGopț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ă fieNU.- 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
NoteDETERMINISTICE = 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 SCHEMABINDINGopț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, ȘIOPENXML)
JOINURI EXTERNE (STÂNGADREAPTA,Tabel derivat (definit prin specificarea unuiNOTFULL)declarație în clauza
SELECT) Auto-joinuriFROMSpecificați coloanele folosind
SELECT *
SELECT .*STDEVsauSTDEVP
DISTINCT
VAR,VARP,Expresie comună a tabelului (CTE),ntextNOTAVG
filestreamfloat1, text, coloane, imagine, XMLNOT Subinterogare PESTE
clauză, care include funcții de fereastră de clasificare sau agregare
Predicați de text complet (CONTAINSFREETEXT
funcție care face referire la o expresie nullable,funcție agregată definită de utilizator CLR)
SUMTOP
ORDER BYSETURI DE GRUPARE
operatori
CUBE,ROLLUPNOTEXCEPTINTERSECT
MIN,MAX
UNION,TABEL SAMPLEDNOTVariabile de tabelINTERSECT
OUTER APPLYCROSS APPLY
PIVOTsauUNPIVOT
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ă. Acesterestricț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ă acesteclauza HAVING cu o subinterogare.restricții.COUNT_BIG(*)Dacă definiția vederii conține oCOUNT_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, ȘIOPENXML)
JOINURI EXTERNE (STÂNGADREAPTA,Tabel derivat (definit prin specificarea unuiNOTFULL)declarație în clauza
SELECT) Auto-joinuriFROMSpecificați coloanele folosind
SELECT *
SELECT .*STDEVsauSTDEVP
DISTINCT
VAR,VARP,Expresie comună a tabelului (CTE),ntextNOTAVG
filestreamfloat1, text, coloane, imagine, XMLNOT Subinterogare PESTE
clauză, care include funcții de fereastră de clasificare sau agregare
Predicați de text complet (CONTAINSFREETEXT
funcție care face referire la o expresie nullable,funcție agregată definită de utilizator CLR)
SUMTOP
ORDER BYSETURI DE GRUPARE
operatori
CUBE,ROLLUPNOTEXCEPTINTERSECT
MIN,MAX
UNION,TABEL SAMPLEDNOTVariabile de tabelINTERSECT
OUTER APPLYCROSS APPLY
PIVOTsauUNPIVOT
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ă . Ș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 , î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 și PostgreSQL, 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
