
В предишната Описах концепцията и реализирането на база данни, построена на основа функции, а не таблици и полета как в релационните бази данни. В нея бяха приведени множество примери, показващи предимствата на такъв подход пред класическия. Много хора ги намериха недостатъчно убедителни.
В тази статия ще покажа как такава концепция позволява бързо и удобно балансиране на записите и четенето в база данни без каквото и да било изменение на логиката на работа. Подобен функционал се опита да бъде реализиран в съвременните търговски СУБД (в частност, Oracle и Microsoft SQL Server). В края на статията ще покажа какво постигнаха те, меко казано, не особено успешно.
Описание
Както и преди, за по-добро разбиране ще започна описанието с примери. Да предположим, че трябва да реализираме логика, която ще връща списък на отделите с броя на служителите в тях и техния общ доход.
В функционалната база данни това ще изглежда по следния начин:
КЛАС Отдел;
име ‘Наименование’ = ДАННИ СТРИНГ[100] (Отдел);
CLASS Employee 'Служител';
department 'Отдел' = DATA Department (Employee);
salary 'Заплата' = DATA NUMERIC[10,2] (Employee);
countEmployees 'Брой служители' (Department d) =
GROUP SUM 1 IF department(Employee e) = d;
salarySum 'Обща заплата' (Department d) =
GROUP SUM salary(Employee e) IF department(e) = d;
SELECT name(Department d), countEmployees(d), salarySum(d);
Сложността на изпълнението на този запит в която и да е СУБД ще бъде равна на O(брой служители), тъй като за това изчисление е необходимо да се прегледа цялата таблица на служителите, а след това да се групират по отдел. Също така ще има някаква малка (да приемем, че служителите са значително повече от отделите) добавка в зависимост от избрания план. O(log брой служители) или O(брой отдели) за групировка и друго.
Ясно е, че разходите за изпълнение могат да бъдат различни в различни СУБД, но сложността няма да се промени по никакъв начин.
В предложената реализация функционалната СУБД ще генерира една под-заявка, която ще изчисли необходимите стойности по отдели, след което ще направи JOIN с таблицата на отделите, за да получи името. Въпреки това, за всяка функция при декларирането има възможност да зададете специален маркер MATERIALIZED. Системата автоматично ще създаде съответно поле за всяка такава функция. При промяна на стойността на функцията, стойността на полето ще се променя в същата транзакция. При достъп до тази функция ще се извършва достъп до предварително изчисленото поле.
По-специално, ако зададете MATERIALIZED за функциите countEmployees и salarySum, тогава в таблицата със списъка на отделите ще бъдат добавени две полета, в които ще се съхраняват броя на служителите и тяхната обща заплата. При всяка промяна на служителите, техните заплати или принадлежността им към отделите, системата автоматично ще променя стойностите на тези полета. Посочената по-горе заявка ще се обръща директно към тези полета и ще се изпълни за O(брой отдели).
Какви ограничения има? Само едно: такова функция трябва да има крайно число входни стойности, за които стойността ѝ е определена. В противен случай няма да е възможно да се построи таблица, съхраняваща всичките ѝ стойности, тъй като не може да има таблица с безкрайно количество редове.
Пример:
employeesCount ‘Брой на служителите с заплата > N’ (Отдел d, NUMERIC[10,2] N) =
GROUP SUM salary(Служител e) IF department(e) = d AND salary(e) > N;
Тази функция е определена за безкрайно количество стойности на числото N (например, всяко отрицателно значение подхожда). Следователно, не може да се зададе MATERIALIZED за нея. По този начин, това е логично, а не техническо ограничение (тоест не защото не сме могли да го реализираме). В останалото — няма ограничения. Можете да използвате групировки, сортирания, AND и OR, PARTITION, рекурсии и т.н.
Например, в задача 2.2 от предишната статия можете да зададете MATERIALIZED за двете функции:
Купил 'Купил' (Клиент c, Продукт p, ЦЕЛО число y) =
ГРУПА СУМ sum(Детайл d) IF
клиент(поръчка(d)) = c И
продукт(d) = p И
извлечиГодина(дата(поръчка(d))) = y МАТЕРИАЛИЗИРАНО;
рейтинг 'Рейтинг' (Клиент c, Продукт p, ЦЕЛО число y) =
ЧАСТИРАНЕ СУМ 1 НАРЕЖДАНЕ ОНД ПРЕДПРИЕТО(c, p, y), p ПО c, y МАТЕРИАЛИЗИРАНО;
ИЗБЕРЕТЕ контактноИме(Клиент c), име(Продукт p) КЪДЕТО рейтинг(c, p, 1997) < 3;
Системата сама ще създаде една таблица с ключове от типове Клиент, Продукт и INTEGER, ще добави в нея две полета и ще обновява стойностите на полетата при всякакви промени. При по-нататъшни извиквания на тези функции, техният изчисление няма да се извършва, а ще се четат стойностите от съответните полета.
С помощта на този механизъм можете, например, да се избавите от рекурсиите (CTE) в заявките. По-специално, да разгледаме групите, които образуват дърво чрез отношението child/parent (всяка група има връзка със своя родител):
родител = DATA Group (Group);
В функционалната база данни логиката на рекурсията може да се зададе по следния начин:
nivå (Gruppa детенце, Gruppa родител) = РЕКУРСИЯ 1l АКО детенцето Е Gruppa И родител == детенцето
СТЪПКА 2l АКО родител == родител($parent);
еРодител (Gruppa детенце, Gruppa родител) = Вярно АКО nivå(детенце, родител) МАТЕРИАЛИЗИРАНО;
Тъй като за функцията isParent е зададен MATERIALIZED, то за нея ще бъде създадена таблица с два ключа (групи), в която полето isParent ще бъде истинно само ако първият ключ е потомък на втория. Броят на записите в тази таблица ще бъде равен на броя на групите, умножен по средната дълбочина на дървото. Ако е необходимо, например, да се изчисли броят на потомците на определена група, може да се обърнете към тази функция:
childrenCount (Група g) = GROUP SUM 1 IF isParent(Група child, g);
Няма да има CTE в SQL заявката. Вместо това ще бъде прост GROUP BY.
С помощта на този механизъм може да се направи лесна денормализация на базата данни при необходимост:
CLASS Order 'Поръчка';
date 'Дата' = DATA DATE (Order);
CLASS OrderDetail 'Ред за поръчка';
order 'Поръчка' = DATA Order (OrderDetail);
date 'Дата' (OrderDetail d) = date(order(d)) MATERIALIZED INDEXED;
При повикване на функцията date за реда на поръчката ще се извършва четене от таблицата с редове за поръчки на полето, за което има индекс. При промяна на датата на поръчката системата автоматично ще прерасчита денормализираната дата в реда.
Предимства
За какво е необходим този механизъм? В класическите СУБД, без преизписване на заявките, разработчикът или DBA могат само да променят индексите, да определят статистиката и да дават насоки на планирача на заявки как да ги изпълнява (като HINT-ите съществуват само в търговските СУБД). Каквото и да правят, те няма да могат да изпълнят първата заявка в статията за O (броя на отделите) без да променят заявките и без добавяне на тригери. В предложената схема, на етапа на разработка не е необходимо да се мисли за структурата на съхранение на данните и какви агрегации да се използват. Всичко това може спокойно да се променя на място в експлоатацията.
На практика това изглежда по следния начин. Някои хора създават логика въз основа на определена задача. Те не разбират ни алгоритми, ни тяхната сложност, ни плановете за изпълнение, ни типовете join, нито каквато и да е друга техническа съставка. Тези хора са по-скоро бизнес анализатори, отколкото разработчици. След това всичко това преминава в тестване или експлоатация. Включва се логване на дълги заявки. Когато се открие дълга заявка, решение за включване на MATERIALIZED на определена междинна функция се взема от други хора (по-технически — всъщност DBA). По този начин записът се забавя малко (тъй като е необходимо обновление на допълнителното поле в транзакцията). Въпреки това, не само тази заявка, но и всички останали, които използват тази функция, значително се ускоряват. При това решението за коя точно функция да се материализира се взема сравнително лесно. Два основни параметъра: брой на възможните входни стойности (точно толкова записи ще има в съответната таблица) и колко често се използва в другите функции.
Аналози
В съвременните комерсиални СУБД съществуват подобни механизми: MATERIALIZED VIEW с FAST REFRESH (Oracle) и INDEXED VIEW (Microsoft SQL Server). В PostgreSQL MATERIALIZED VIEW не може да се обновява в транзакция, а само по запитване (може би с много строги ограничения), така че няма да я разглеждаме. Но те имат няколко проблема, които значително ограничават тяхното използване.
На първо място, материализацията може да се включи само, ако вече е създадена обикновена VIEW. В противен случай ще е необходимо да се пренапишат останалите запитвания, за да се обърнат към новосъздаденото представление, за да може да се използва тази материализация. Или да се остави всичко както е, но това ще бъде поне неефективно, ако вече има определени предварително изчислени данни, но много запитвания не винаги ги използват, а ги изчисляват отново.
На второ място, те имат огромно количество ограничения:
Oracle
5.3.8.4 Общи ограничения на бързото обновление
Определящото запитване на материализираното представление е ограничено, както следва:
- Материализираното представление не трябва да съдържа референции към не повтарящи се изрази като
SYSDATEandROWNUM.- Материализираното представление не трябва да съдържа референции към
RAWorLONGRAWтипове данни.- Не може да съдържа
SELECTсубзаявка в списък.- Не може да съдържа аналитични функции (например,
RANK) вSELECTклауза.- Не може да се позовава на таблица, върху която е дефиниран
XMLIndexиндекс.- Не може да съдържа
MODELклауза.- Не може да съдържа
клауза HAVING с подзапитване.Не може да съдържа вложени запитвания, които имат- ANY
, или,ALL[START WITH …] CONNECT BYNOTEXISTS.- Не може да съдържа
[START WITH …] СВЪРЗВАНЕ ЧРЕЗклауза.- Не може да съдържа множество детайлни таблици на различни сайтове.
ONCOMMITМатериализираните изгледи не могат да имат отдалечени детайлни таблици.- Вложените материализирани изгледи трябва да имат присъединяване или агрегация.
- Материализирани присъединяващи изгледи и материализирани агрегационни изгледи с
ГРУПАПОклаузата не може да избира от таблица, организирана по индекс.5.3.8.5 Ограничения за бързо обновление на материализирани изгледи само с присъединявания.
Определящите запитвания за материализирани изгледи само с присъединявания и без агрегации имат следните ограничения за бързо обновление:
- Всички ограничения от ««.
- Те не могат да имат
ГРУПАПОклаузи или агрегации.- Rowid-ите на всички таблици в
FROMсписъка трябва да се появят вSELECTсписъка на запитването.- Логовете на материализираните изгледи трябва да съществуват с rowid-ите за всички основни таблици в
FROMсписъка на запитването.- Не можете да създадете материализиран изглед, който да се обновява бързо от множество таблици с прости присъединявания, които включват колона от тип обект в
SELECTизявлението.Също така, методът за обновление, който изберете, няма да бъде оптимално ефективен, ако:
- Определящото запитване използва външно присъединяване, което се държи като вътрешно присъединяване. Ако определящото запитване съдържа такова присъединяване, помислете за пренаписване на определящото запитване, за да съдържа вътрешно присъединяване.
- The
SELECTсписъкът на материализирания изглед съдържа изрази върху колони от множество таблици.5.3.8.6 Ограничения за бързо обновление на материализирани изгледи с агрегации.
Определящите запитвания за материализирани изгледи с агрегации или присъединявания имат следните ограничения за бързо обновление:
- Всички ограничения от ««.
Бързото обновление се поддържа за двете
ONCOMMITandONПОИСКВАНЕматериализирани изгледи, обаче следните ограничения се прилагат:
- Всички таблици в материализирания изглед трябва да имат логове на материализирани изгледи, а логовете на материализирания изглед трябва:
- Да съдържат всички колони от таблицата, спомената в материализирания изглед.
- Да се обозначават с
ROWIDandВКЛЮЧИТЕЛНОНОВИVALUES.- Да се обозначи
ПОСЛЕДОВАТЕЛНОСТклаузата, ако се очаква, че таблицата ще има смес от вмъквания/директни натоварвания, изтривания и актуализации.- Само
СУМ,COUNT,AVG,СТДДЕВ,ВАРИАЦИЯ,МИНandMAXса поддържани за бързо обновление.COUNT(*)трябва да бъде указан.- Агрегираните функции трябва да се появяват само като най-външната част на израза. Тоест, агрегати като
AVG(AVG(x))orAVG(x)+AVG(x)не са разрешени.- За всеки агрегат, като
AVG(expr), съответстващиятCOUNT(expr)трябва да присъства. Oracle препоръчва, чеSUM(expr)да бъде указан.- Ако
VARIANCE(expr)orСТДДЕВ(expr) се укаже,COUNT(expr)andSUM(expr)трябва да бъде указан. Oracle препоръчва, чеSUM(expr * expr)да бъде указан.- The
SELECTколоната в определящото запитване не може да бъде сложен израз с колони от множество основни таблици. Една възможна алтернатива за това е да се използва вложен материализиран изглед.- The
SELECTсписъкът трябва да съдържа всичкиГРУПАПОколони.- Материализираният изглед не е основан на една или повече отдалечени таблици.
- Ако използвате
CHARтип данни в колоните за филтриране на лог на материализиран изглед, наборите от символи на основния сайт и материализирания изглед трябва да са същите.- Ако материализираният изглед има един от следните, тогава бързото обновление се поддържа само при конвенционални DML вмъквания и директни натоварвания.
- Материализирани изгледи с
МИНorMAXагрегати- Материализирани изгледи, които имат
SUM(expr)но неCOUNT(expr)- Материализирани изгледи без
COUNT(*)Такъв материализиран изглед се нарича само-вмъкващ материализиран изглед.
- Материализираният изглед с
MAXorМИНсе обновява бързо след изтриване или смесени DML изявления, ако не съдържаWHEREклауза.
Максималното/минималното бързо обновление след изтриване или смесени DML няма същото поведение като случая при само-вмъквания. Той изтрива и преизчислява максималните/минималните стойности за засегнатите групи. Трябва да сте наясно с неговото влияние върху производителността.- Материализирани изгледи с именувани изгледи или подзапитвания в
FROMклаузата могат да се обновят бързо при условие, че изгледите могат да бъдат напълно слети. За информация за това кои изгледи ще се слеят, вижте .- Ако няма външни присъединявания, може да имате произволни селекции и присъединявания в
WHEREклауза.- Материализирани агрегатни изгледи с външни присъединявания могат да се обновят бързо след конвенционални DML и директни натоварвания, при условие че само външната таблица е била променена. Също така, уникални ограничения трябва да съществуват по колоните на присъединителната таблица в вътрешното присъединяване. Ако има външни присъединявания, всички присъединявания трябва да са свързани.
Ии трябва да използва оператора за равенство (=) оператор.- За материализирани изгледи с
CUBE,ROLLUP, груповите набори или тяхната комбинация, се прилагат следните ограничения:
- The
SELECTсписъкът трябва да съдържа уникализатор на групировка, който може да бъдеGROUPING_IDфункция за всичкиГРУПАПОизрази илиGROUPINGфункции за всекиГРУПАПОизраз. Например, акоГРУПАПОклауза на материализирания изглед е «ГРУПАПОCUBE(a, b)«, то списъкът трябва да съдържа или «SELECTGROUPING_ID(a, b)» или «GROUPING(a)GROUPING(b)И» за материализирания изглед да бъде бързо обновяем.не трябва да доведе до дублиращи се групировки. Например, «ГРУПАПОGROUP BY a, ROLLUP(a, b)» не е бързо обновяем, тъй като води до дублиращи се групировки «(a), (a, b), И (a)5.3.8.7 Ограничения по отношение на бързо обновление на материализирани изгледи с UNION ALL«.Материализирани изгледи с
оператор за множество поддържат
UNIONALLREFRESHFASTопцията, ако са изпълнени следните условия:Дефиниращата заявка трябва да има
- оператор на горно ниво.
UNIONALLоператорът не може да бъде вграждан в подзаявка, с едно изключение: ТозиThe
UNIONALLможе да бъде в подзаявка вUNIONALLклауза, при условие че дефиниращата заявка е от форматаFROMSELECT * FROM(изглед или подзаявка с) как във следния пример:UNIONALLCREATE 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;Обърнете внимание, че изгледътview_with_unionall
спазва изискванията за бързо обновление.Всеки блок от запитването трябва да отговаря на изискванията за бързо обновяем материализиран изглед с агрегати или за бързо обновяем материализиран изглед с обединения.- Необходимите материалирани дневници на изгледа трябва да бъдат създадени върху масите, както е необходимо за съответния тип бързо обновяем материализиран изглед.
UNIONALLОбърнете внимание, че Oracle Database също позволява специалния случай на материализиран изглед с единствена таблица с обединения само при условие, чеколоната е била включена в
списъка и в дневника на материализирания изглед. Това е показано в дефиниращата заявка на изгледаROWIDсписъкът на всяко запитване трябва да включва уникализатор, и колонатаSELECTтрябва да има различителна константна числова или стрингова стойност във всякаспазва изискванията за бързо обновление..- The
SELECTклонка. Освен това, колоната с уникализатора трябва да се появи на същата поредна позиция вUNIONALLсписъка на всеки блок от запитването. Вижте «UNIONALLUNION ALL Marker and Query RewriteUNIONALL» за повече информация относноSELECTуникализаторите.. Обаче, материализирани изгледи, използвани в репликация, които не съдържат обединения или агрегати, могат да бъдат бързо обновени, когатоUNIONALLили отдалечени таблици се използват.- Параметърът за инициализация на съвместимостта трябва да бъде зададен на 9.2.0 или по-висока версия, за да се създаде бързо обновяем материализиран изглед с
UNIONALLНе искам да обидя феновете на Oracle, но на базата на техния списък с ограничения, създава се впечатлението, че този механизъм не е написан в общия случай, използвайки някаква моделна схема, а от хиляди индийци, на които е било дадено позволение да напишат своя клон, и всеки е направил каквото е могъл. Използването на този механизъм за реална логика — това е като ходене по мини. Във всякакъв момент може да попаднете на мина, попадайки на едно от неочевидните ограничения. Как функционира — също е отделен въпрос, но той е извън обхвата на настоящата статия.UNIONALLДопълнителни изисквания- Освен опции SET и изискванията за детерминирани функции, следните изисквания трябва да бъдат изпълнени:
UNIONALL.
Потребителят, който изпълнява
Microsoft SQL Server
Допълнителни изисквания
В допълнение към изискванията за SET опции и детерминирани функции, трябва да бъдат изпълнени следните изисквания:
- Потребителят, който изпълнява
CREATE INDEXтрябва да бъдете собственик на изгледа.- Когато създавате индекса,
IGNORE_DUP_KEYопцията трябва да бъде зададена на ИЗКЛЮЧЕНО (по подразбиране).- Таблиците трябва да бъдат посочвани с двусъставни имена, schema.tablename в определението за изгледа.
- Потребителските функции, посочени в изгледа, трябва да бъдат създадени с
WITH SCHEMABINDINGопция.- Всяка потребителска функция, посочена в изгледа, трябва да бъде посочена с двусъставни имена, <схема>.<функция>.
- Свойството за достъп до данни на потребителска функция трябва да бъде
NO SQL, а свойството за външен достъп трябва да бъдеNO.- Функции на общия език за програмиране (CLR) могат да се появят в списъка за избор на изгледа, но не могат да бъдат част от определението на ключа на кластеризирания индекс. CLR функции не могат да се появяват в клаузата WHERE на изгледа или в клаузата ON на операция JOIN в изгледа.
- Функции CLR и методи на потребителски типове, определени от CLR, използвани в определението за изгледа, трябва да имат свойствата зададени по начина, показан в следната таблица.
Property
NoteDETERMINISTIC = TRUE
Трябва да бъде декларирано изрично като атрибут на метода от Microsoft .NET Framework.PRECISE = TRUE
Трябва да бъде декларирано изрично като атрибут на метода от .NET Framework.DATA ACCESS = NO SQL
Определя се чрез задаване на атрибута DataAccess на DataAccessKind.None и атрибута SystemDataAccess на SystemDataAccessKind.None.EXTERNAL ACCESS = NO
Тази собственост по подразбиране е NO за CLR рутините.- Изгледът трябва да бъде създаден с
WITH SCHEMABINDINGопция.- Изгледът трябва да се отнася само за базови таблици, които се намират в същата база данни като изгледа. Изгледът не може да се отнася до други изгледи.
- SELECT изразът в определението за изгледа не трябва да съдържа следните елементи на Transact-SQL:
COUNT
Функции ROWSET (OPENDATASOURCE,OPENQUERY,OPENROWSET, ИOPENXML)
ВЪНШНИсъединения (ЛЯВ,ДЯСЕН[START WITH …] CONNECT BYFULL)Производна таблица (определена чрез задаване на
SELECTизраз вFROMклаузата)
Самосъединения
Посочване на колони чрезSELECT *orSELECT <име_на_таблица>.*
DISTINCT
STDEV,STDEVP,VAR,VARP[START WITH …] CONNECT BYAVG
Обща таблица израз (CTE)float1, text, ntext, image, XML[START WITH …] CONNECT BY filestream колони
Подзаявка
OVERклаузата, която включва функции за класиране или обобщаващи функции за прозорецПредикати с пълен текст (
CONTAINS,FREETEXT)
СУМфункция, която се отнася до нулаем израз
ORDER BYCLR потребителска агрегатна функция
TOP
CUBE,ROLLUP[START WITH …] CONNECT BYGROUPING SETSоператори
МИН,MAX
UNION,EXCEPT[START WITH …] CONNECT BYINTERSECTоператори
TABLESAMPLEТаблични променливи
OUTER APPLYorCROSS APPLY
PIVOT,UNPIVOTСетове от редове с рядка колона
Инлайн (TVF) или многоизявление таблици с функции (MSTVF)
OFFSET
CHECKSUM_AGG1 Индексираният изглед може да съдържа float колони; обаче, такива колони не могат да бъдат включени в ключа на кластеризирания индекс.
- Ако
GROUP BYако е налице, определението на ИЗГЛЕДА трябва да съдържаCOUNT_BIG(*)и не трябва да съдържаклауза HAVING с подзапитване.. ТезиGROUP BYограничения се прилагат само за определението на индексирания изглед. Запитването може да използва индексирания изглед в плана за изпълнение, дори ако не удоволетворява тезиGROUP BYограничения.- Ако определението на изгледа съдържа
GROUP BYклаузата, ключът на уникалния кластеризиран индекс може да се отнася само за колоните, посочени вGROUP BYклауза.
Тук е видно, че индийците не са били привлечени, тъй като решиха да действат по схемата "да направим малко, но добре". Тоест те имат повече мини на полето, но разположението им е по-прозрачно. Най-много ме разочарова това ограничение:
Изгледът трябва да се отнася само за базови таблици, които се намират в същата база данни като изгледа. Изгледът не може да се отнася до други изгледи.
В нашата терминология това означава, че функцията не може да се отнася до друга материализирана функция. Това реже цялата идея в корена.
Също така това ограничение (и следващото по текста) намалява много вариантите за използване:
SELECT изразът в определението за изгледа не трябва да съдържа следните елементи на Transact-SQL:
COUNT
Функции ROWSET (OPENDATASOURCE,OPENQUERY,OPENROWSET, ИOPENXML)
ВЪНШНИсъединения (ЛЯВ,ДЯСЕН[START WITH …] CONNECT BYFULL)Производна таблица (определена чрез задаване на
SELECTизраз вFROMклаузата)
Самосъединения
Посочване на колони чрезSELECT *orSELECT <име_на_таблица>.*
DISTINCT
STDEV,STDEVP,VAR,VARP[START WITH …] CONNECT BYAVG
Обща таблица израз (CTE)float1, text, ntext, image, XML[START WITH …] CONNECT BY filestream колони
Подзаявка
OVERклаузата, която включва функции за класиране или обобщаващи функции за прозорецПредикати с пълен текст (
CONTAINS,FREETEXT)
СУМфункция, която се отнася до нулаем израз
ORDER BYCLR потребителска агрегатна функция
TOP
CUBE,ROLLUP[START WITH …] CONNECT BYGROUPING SETSоператори
МИН,MAX
UNION,EXCEPT[START WITH …] CONNECT BYINTERSECTоператори
TABLESAMPLEТаблични променливи
OUTER APPLYorCROSS APPLY
PIVOT,UNPIVOTСетове от редове с рядка колона
Инлайн (TVF) или многоизявление таблици с функции (MSTVF)
OFFSET
CHECKSUM_AGG
Забранени са ВЪНШНИ СЪЕДИНЕНИЯ, UNION, ORDER BY и други. Може би е било по-лесно да се посочи какво може да се използва, отколкото какво не може. Списъкът вероятно щеше да бъде много по-кратък.
В заключение: значительное количество ограничений в каждой (отмечу коммерческой) СУБД против отсутствия таковых (за исключением одного логического, а не технического) в технологии LGPL. Однако стоит отметить, что внедрение этого механизма в реляционной логике немного сложнее, чем описано в функциональной.
Реализация
Как это работает? В качестве «виртуальной машины» используется PostgreSQL. Внутри находится сложный алгоритм, занимающийся построением запросов. Вот что. И там не просто обширный набор эвристик с множеством условий. Так что, если у вас есть несколько месяцев на изучение, вы можете попробовать разобраться в архитектуре.
Работает ли это эффективно? Достаточно эффективно. К сожалению, доказать это сложно. Могу лишь сказать, что если рассмотреть тысячи запросов, используемых в крупных приложениях, то в среднем они эффективнее, чем у хорошего разработчика. Отличный SQL-программист может написать любой запрос более эффективно, но на тысяче запросов у него просто не будет ни времени, ни мотивации это делать. Единственное, что могу предложить как доказательство эффективности — это то, что на платформе, созданной на этой СУБД, работают несколько проектов. , в которых существует тысячи различных MATERIALIZED функций, с тысячами пользователей и терабайтными базами данных с сотнями миллионов записей, функционирующими на обычном двухпроцессорном сервере. Тем не менее, любой желающий может проверить или опровергнуть эффективность, скачав и PostgreSQL, включая логирование SQL-запросов и попробовав изменять там логику и данные.
В следующих статьях я также расскажу о том, как можно накладывать ограничения на функции, работать с сеансами изменений и многое другое.
Източник: habr.com
