Балансіроўка запісу і чытання ў базе даных

Балансіроўка запісу і чытання ў базе даных
У папярэдняй артыкуле я апісаў канцэпцыю і рэалізацыю базы дадзеных, пабудаваную на аснове функцый, а не табліц і палёў як у рэляцыйных базах дадзеных. У ёй было прыведзена мноства прыкладаў, якія паказваюць перавагі такога падыходу перад класічным. Многія палічылі іх недастаткова пераканаўчымі.

У гэтым артыкуле я пакажу, якім чынам такая канцэпцыя дазваляе хутка і зручна балансаваць запіс і чытанне ў базу даных без якой-небудзь змены логікі працы. Падобны функцыянал паспрабавалі рэалізаваць у сучасных камерцыйных СКБД (у прыватнасці, Oracle і Microsoft SQL Server). У канцы артыкула я пакажу, што атрымалася ў іх, мякка кажучы, ня надта.

Апісанне

Як і раней, для лепшага разумення я пачну апісанне на прыкладах. Дапусцім, нам трэба рэалізаваць логіку, якая будзе вяртаць спіс аддзелаў з колькасцю супрацоўнікаў у іх і іх сумарным заробкам.

У функцыянальнай базе дадзеных гэта будзе выглядаць наступным чынам:

CLASS Department ‘Отдел’;
name ‘Наименование’ = DATA STRING[100] (Department);

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’ (Department d, NUMERIC[10,2] N) = 
    GROUP SUM salary(Employee e) IF department(e) = d AND salary(e) > N;

Гэтая функцыя вызначана для бясконцай колькасці значэнняў ліку N (напрыклад, падыходзіць любое адмоўнае значэнне). Таму на яе нельга паставіць MATERIALIZED. Такім чынам, гэтае лагічнае, а не тэхнічнае абмежаванне (гэта значыць не таму, што мы не змаглі гэта рэалізаваць). У астатнім - ніякіх абмежаванняў. Можна выкарыстоўваць групоўкі, сартавання, AND і OR, PARTITION, рэкурсіі і г.д.

Напрыклад, у задачы 2.2 папярэдняга артыкула можна паставіць MATERIALIZED на абедзве функцыі:

bought 'Купил' (Customer c, Product p, INTEGER y) = 
    GROUP SUM sum(Detail d) IF 
        customer(order(d)) = c AND 
        product(d) = p AND 
        extractYear(date(order(d))) = y MATERIALIZED;
rating 'Рейтинг' (Customer c, Product p, INTEGER y) = 
    PARTITION SUM 1 ORDER DESC bought(c, p, y), p BY c, y MATERIALIZED;
SELECT contactName(Customer c), name(Product p) WHERE rating(c, p, 1997) < 3;

Сістэма сама створыць адну табліцу з ключамі тыпаў Кліент, прадукт и INTEGER, дадасць у яе два палі і будзе абнаўляць у іх значэння палёў пры любых зменах. Пры далейшых зваротах да гэтых функцый не будзе адбывацца іх разлік, а будуць счытвацца значэнні з адпаведных палёў.

Пры дапамозе гэтага механізму можна, напрыклад, пазбаўляцца ад рэкурсій (CTE) у запытах. У прыватнасці, разгледзім групы, якія ўтвараюць дрэва пры дапамозе стаўлення child/parent (у кожнай групы ёсць спасылка на свайго бацьку):

parent = DATA Group (Group);

У функцыянальнай базе дадзеных логіку рэкурсій можна задаць наступным чынам:

level (Group child, Group parent) = RECURSION 1l IF child IS Group AND parent == child
                                                             STEP 2l IF parent == parent($parent);
isParent (Group child, Group parent) = TRUE IF level(child, parent) MATERIALIZED;

Бо для функцыі isParent прастаўлены MATERIALIZED, то пад яе будзе створана табліца з двума ключамі (групамі), у якой поле isParent будзе сапраўдным толькі, калі першы ключ з'яўляецца нашчадкам другога. Колькасць запісаў у гэтай табліцы будзе роўна колькасці груп, памножанай на сярэднюю глыбіню дрэва. Калі неабходна, напрыклад, палічыць колькасць нашчадкаў вызначанай групы, то можна звяртацца да гэтай функцыі:

childrenCount (Group g) = GROUP SUM 1 IF isParent(Group 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;

Пры звароце да функцыі дата для радка замовы будзе ісці чытанне з табліцы з радкамі заказаў поля, па якім ёсць азначнік. Пры змене даты замовы сістэма будзе сама аўтаматычна пералічваць дэнармалізаваную дату ў радку.

Перавагі

Навошта ўвесь гэты механізм патрэбен? У класічных СКБД, без перапісвання запытаў, распрацоўшчык або DBA могуць толькі змяняць індэксы, вызначаць статыстыку і падказваць планавальніку запытаў, як іх выконваць (прычым HINT'ы ёсць толькі ў камерцыйных СКБД). Як бы яны не імкнуліся, яны не змогуць першы запыт у артыкуле выканаць за Аб (кольк-ць аддзелаў) без змены запытаў і дапісванні трыгераў. У прапанаванай жа схеме, на этапе распрацоўкі можна не задумвацца аб структуры захоўвання дадзеных і аб тым, якія агрэгацыі выкарыстоўваць. Гэта ўсё можна спакойна мяняць на лёце ўжо непасрэдна ў эксплуатацыі.

На практыцы гэта выглядае наступным чынам. Некаторыя людзі распрацоўваюць непасрэдна логіку на аснове пастаўленай задачы. Яны не разбіраюцца ні ў алгарытмах і іх складанасці, ні ў планах выканання, ні ў тыпах join'ов, ні ў любым іншым тэхнічным складніку. Гэтыя людзі - хутчэй бізнес-аналітыкі, чым распрацоўшчыкі. Затым, усё гэта ідзе ў тэсціраванне або эксплуатацыю. Уключаецца лагіраванне працяглых запытаў. Калі выяўляецца доўгі запыт, то ўжо іншымі людзьмі (больш тэхнічнымі - у сутнасці DBA) прымаецца рашэнне аб уключэнні MATERIALIZED на некаторай прамежкавай функцыі. Тым самым крыху запавольваецца запіс (бо патрабуецца абнаўленне дадатковага поля ў транзакцыі). Аднак значна паскараецца не толькі гэты запыт, але і ўсе іншыя, якія выкарыстоўваюць гэтую функцыю. Пры гэтым прыняцце рашэння аб тым, якую менавіта функцыю матэрыялізаваць прымаецца адносна нескладана. Два асноўныя параметры: у магчымых уваходных значэнняў (менавіта столькі запісаў будзе ў адпаведнай табліцы), і наколькі часта яна выкарыстоўваецца ў іншых функцыях.

аналагі

У сучасных камерцыйных СКБД ёсць падобныя механізмы: MATERIALIZED VIEW з FAST REFRESH (Oracle) і INDEXED VIEW (Microsoft SQL Server). У PostgreSQL MATERIALIZED VIEW не ўмее абнаўляцца ў транзакцыі, а толькі па запыце (ды яшчэ з зусім цвёрдымі абмежаваннямі), так што яго не разгляданы. Але ў іх ёсць некалькі праблем, што значна абмяжоўвае іх выкарыстанне.

Па-першае, можна ўключыць матэрыялізацыю толькі, калі ў вас ужо быў створаны звычайны VIEW. Інакш давядзецца перапісваць астатнія запыты на зварот да зноў створанага прадстаўлення, каб выкарыстоўваць гэтую матэрыялізацыю. Або пакінуць усё як ёсць, але будзе як мінімум неэфектыўна, калі ёсць пэўныя ўжо падлічаныя дадзеныя, але многія запыты іх не заўсёды выкарыстоўваюць, а вылічваюць нанава.

Па-другое, у іх ёсць вялікая колькасць абмежаванняў:

Аракул

5.3.8.4 General Restrictions on Fast Refresh

Definition query of materialized view restricted as follows:

  • Materialized view must no contain references to non-repeating expressions like SYSDATE і ROWNUM.
  • Materialized view must no contain references to RAW or LONG RAW тыпы даных.
  • It cannot contain a SELECT list subquery.
  • It cannot contain аналагічныя функцыі (для example, RANK) У SELECT пункт.
  • It cannot reference a table on which an XMLIndex index is defined.
  • It cannot contain a MODEL пункт.
  • It cannot contain a HAVING clause with a subquery.
  • It cannot contain nested queries that have ANY, ALLабо NOT EXISTS.
  • It cannot contain a [START WITH …] CONNECT BY пункт.
  • It cannot contain multiple detail tables at different sites.
  • ON COMMIT матэрыяльных відаў cannot have remote detail tables.
  • Патрэбныя матэрыяльныя віды павінны мець патрэбу або агрэгаты.
  • Materialized join views and materialized aggregate views with a GROUP BY clause cannot select from an index-organized table.

5.3.8.5 Restrictions on Fast Refresh on Materialized Views with Joins Only

Defining queries for materialized views with joins only and no aggregates have the following restrictions on fast refresh:

  • All restrictions from «General Restrictions on Fast Refresh«.
  • They cannot have GROUP BY clauses or aggregates.
  • Шляхі ўсіх tables in the FROM list must appear in the SELECT list of the query.
  • Materialized view logs must exist with rowids for all the base tables in the FROM list of the query.
  • Вы не можаце стварыць рэзкі абмежаваны матэрыяльны выгляд ад некалькіх таргоў з нядрэннымі joins, якія ахопліваюць object type column in the SELECT заяву.

Also, the refresh method you choose will no be optimally efficient if:

  • Definition query use outer join that behaves like an inner join. Калі вы выбіраеце патрабаванні, якія павінны быць у курсе, з'яўляецеся, што трэба запісаць патрабаванні, якія будуць размяшчацца ў іншым месцы.
  • ,en SELECT аркуш матэрыялізаваных відовішчаў змяшчае expresны на аркушах з некалькіх tables.

5.3.8.6 Restrictions on Fast Refresh on Materialized Views with Aggregates

Defining queries for materialized views with agregates or joins have the following restrictions on fast refresh:

Fast refresh is supported for both ON COMMIT і ON DEMAND матэрыяльных відавак, перад тымі пярэчанні restrictions apply:

  • Усе аркушы ў матэрыяльным выглядзе павінны мець materialized view logs, and materialized view logs must:
    • Маюцца ўсе columnы з табліцы, змешчанай у матэрыяльным выглядзе.
    • Specify with ROWID і INCLUDING NEW VALUES.
    • пакажыце SEQUENCE clause, калі table expected to ha a mix of inserts/direct-loads, deletes, and updates.

  • Толькі SUM, COUNT, AVG, STDDEV, VARIANCE, MIN і MAX supported for fast refresh.
  • COUNT(*) be specified.
  • Aggregate functions must occur only as the outermost part of the expression. That is, aggregates such as AVG(AVG(x)) or AVG(x)+ AVG(x) не дапускаюцца.
  • Для ўсіх пазбаўляецца так як AVG(expr), адпаведная COUNT(expr) be be present. Oracle recommends that SUM(expr) tatoeba be specified.
  • If VARIANCE(expr) or STDDEV(expr) is specified, COUNT(expr) і SUM(expr) be specified. Oracle recommends that SUM(expr *expr) tatoeba be specified.
  • ,en SELECT column in the defining query cannot be complex expression with columns from multiple base tables. Магчымая праца ў гэтым выкарыстоўваецца для матэрыяльнага прагляду.
  • ,en SELECT list must contain all GROUP BY columns.
  • Матэрыяльны погляд не падыходзіць на адной або больш remote tables.
  • Калі вы выкарыстоўваеце CHAR data type in the filter columns materialized view log, character sets of the master site and materialized view must be the same.
  • Калі матэрыялізаваны выгляд мае адзін з наступных, то невялікая раптоўнасць з'яўляецца толькі на фундацыйныя DML inserts і direct loads.
    • Materialized views with MIN or MAX агрэгаты
    • Materialized views which have SUM(expr) але не COUNT(expr)
    • Materialized views without COUNT(*)

    Такі матэрыялізаваны погляд мае назву insert-only materialized view.

  • A materialized view with MAX or MIN з'явиться швидка репресійно після усунення або mixed DML statements if it does not have a WHERE пункт.
    Макс./мін. макс. раптоўна павышэнне пасля пазбаўленасці або міксаванай DML не павінна мець тыя ж пазыкі, як insert-only case. Гэта deletes and recomputes max/min. Вы павінны патрабаваць яе эфектыўнасці impact.
  • Materialized views with named views or subqueries in the FROM clause can be fast refreshed выстаўлены views can be completely merged. For information on which views will merge, viz Oracle Database SQL Language Reference.
  • Калі ў вас няма ўласных joins, вы можаце мець arbitrary selections and joins in the WHERE пункт.
  • Матэрыялістыкі ахопліваючыя аўдыторыі з дзвюма прынадміннямі з'яўляюцца хуткімі freshable po conventional DML і direct loads, ажыццёўленыя толькі ў дзвюх table будуць modifikované. Also, unique constraints must exist on join columns inner join table. If there are outer joins, all the joins must be connected by ANDs and must use the equality (=) operator.
  • Для матэрыяльных відовішчаў CUBE, ROLLUP, grouping sets, or concatenation of them, the following restrictions apply:
    • ,en SELECT List павінен contain grouping distinguisher that can either be a GROUPING_ID function on all GROUP BY expressions or GROUPING functions one for each GROUP BY expression. Для таго, каб GROUP BY clause of materialized view is «GROUP BY CUBE(a, b)«, then the SELECT list should contain either «GROUPING_ID(a, b)» або «GROUPING(a) AND GROUPING(b)» для матэрыяльнага прагляду для таго, каб быць рэзкімі.
    • GROUP BY не павінны выконваць у любым выпадку duplicate groupings. For example, «GROUP BY a, ROLLUP(a, b)» is not fast refreshable because it results in duplicate groupings «(a), (a, b), AND (a)«.

5.3.8.7 Restrictions on Fast Refresh on Materialized Views with UNION ALL

Materialized views with the UNION ALL set operator support the REFRESH FAST option if the following conditions are satisfied:

  • The defining query must have the UNION ALL абаронца на верхнім узроўні.

    ,en UNION ALL абаронца не можа быць embedded uvnitř subquery, with one exception: The UNION ALL can be in a subquery in the FROM clause provided the defining query is of the form SELECT * FROM (view or subquery with UNION ALL) as in the following example:

    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 FROMcus 'Jones'); CREATE MATERIALIZED VIEW unionall_inside_view_mv REFRESH FAST ON DEMAND AS SELECT * FROM view_with_unionall;
    

    Note that the view view_with_unionall satisface the requirements for fast refresh.

  • Each query block in the UNION ALL дбайна мусіць адпавядаць неабходным патрабаванням рэзкага абмежаванага матэрыяльнага выгляду з агрэгатамі або рэзкім абмежаваным матэрыялізаваным view with joins.

    Прыдатны матэрыялізаваны выгляд logs павінны быць створаны на Tables як патрабуецца для падобных тыпу рэзкага парэзальнага матэрыяльнага прагляду.
    Зверніть увагу, што Oracle Database таксама дазваляе асаблівую частку цэлых XNUMX table materialized view with joins only provided the ROWID column has been included in the SELECT аркуш і ў матэрыялізаванай аналітычнай форме. Гэта shown in the defining query of the view view_with_unionall.

  • ,en SELECT List of each query must include a UNION ALL marker, and the UNION ALL column павінен мець адметную constant numeric or string value in each UNION ALL branch. Further, marker column мусіць падаць у самім парадным становішчы in the SELECT list of each query block. See «UNION ALL Marker and Query Rewrite» for more information regarding UNION ALL маркеры.
  • Некаторыя асаблівасці такія, як два джентльмена, insert-only aggregate materialized view queries and remote tables not supported for materialized views with UNION ALL. Адчувае, цудоўна, што матэрыялізаваныя выданні выкарыстоўваюцца ў апеляцыі, якія не маюць патрэбы або aggregates, могуць быць рэзка абмежаваныя калі UNION ALL or remote tables are used.
  • Compatibility initialization parametr musí byť nastavený na 9.2.0 або максімум для стварэння хуткага абмежаванага матэрыяльнага прагляду з UNION ALL.

Не жадаю пакрыўдзіць прыхільнікаў Oracle, але судзячы па іх спісу абмежаванняў, ствараецца ўражанне, што гэты механізм пісалі не ў агульным выпадку, выкарыстаючы нейкую мадэль, а тысячы індусаў, дзе кожнаму далечы пісаць сваю галінку, і кожны з іх што змог, то і зрабіў. Выкарыстанне гэтага механізму для рэальнай логікі - гэта як хаджэнне па мінным полі. У любы момант можна атрымаць міну, патрапіўшы на адно з не відавочных абмежаванняў. Як гэта працуе – таксама асобнае пытанне, але яно знаходзіцца па-за рамкамі дадзенага артыкула.

Microsoft SQL Server

дадатковыя патрабаванні

У сувязі з тым, што SET options and deterministic function requirements, the following requirements must be met:

  • The user that executes CREATE INDEX павінен быць ўладальнікам пазнакі.
  • When you create the index, the IGNORE_DUP_KEY option мусіць быць выкананы (OFF).
  • Tables must be referenced by two-part names, схема.імя табліцы in the view definition.
  • User-defined functions referenced in the view must be created by using the WITH SCHEMABINDING варыянт.
  • Адзін user-defined functions referenced in the view must be referenced by two-part names, ..
  • The data access property of a user-defined function must be NO SQL, and external access property must be NO.
  • Асноўныя функцыі runtime (CLR) функцый можна пазнаць у select list list of view, but cannot be part of the definition of clustered index key. CLR functions cannot appear in WHERE clause of the view or the ON clause of JOIN operation in the view.
  • CLR функцый і метадаў CLR user-defined types usade in view definition must have the properties set as shown in the following table.

    Уласнасць
    Нататка

    DETERMINISTIC = TRUE
    Muse be declared explicitly as atribut of the Microsoft .NET Framework method.

    PRECISE = TRUE
    Muse be declared explicitly as attribute of .NET Framework method.

    DATA ACCESS = NO SQL
    Determined by setting DataAccess attribute to DataAccessKind.None and SystemDataAccess attribute to SystemDataAccessKind.None.

    EXTERNAL ACCESS = NO
    Гэтыя property defaults to NO for CLR routines.

  • The view must be created by using the WITH SCHEMABINDING варыянт.
  • View must reference only base tables які ў той жа самыя 데이터베이스 як view. view cannot reference іншых views.
  • SELECT statement in view definition not contain the following Transact-SQL elements:

    COUNT
    ROWSET functions (OPENDATASOURCE, OPENQUERY, OPENROWSETІ OPENXML)
    OUTER joins (LEFT, RIGHTабо FULL)

    Derived table (defined by specifying a SELECT заяву ў FROM clause)
    Self-joins
    Specifying columns by using SELECT * or SELECT <table_name>.*

    DISTINCT
    STDEV, STDEVP, VAR, VARPабо AVG
    Common table expression (CTE)

    плаваць1, тэкст, ntext, малюнак, XMLабо filestream слупкоў
    Падзапыт
    OVER clause, which includes ranking or aggregate window functions

    Full-text predicates (CONTAINS, FREETEXT)
    SUM function that references a nullable expresion
    ORDER BY

    CLR user-defined aggregate function
    TOP
    CUBE, ROLLUPабо GROUPING SETS аператары

    MIN, MAX
    UNION, EXCEPTабо INTERSECT аператары
    TABLESAMPLE

    Table variables
    OUTER APPLY or CROSS APPLY
    PIVOT, UNPIVOT

    Sparse column sets
    Inline (TVF) або мульты-статэмент table-valued functions (MSTVF)
    OFFSET

    CHECKSUM_AGG

    1 Indexed view can contain плаваць columns; however, such columns cannot be included in clustered index key.

  • If GROUP BY is present, the VIEW definition must contain COUNT_BIG(*) and must not contain HAVING. Гэтыя GROUP BY Restrictions можна выкарыстоўваць толькі да indexed view definition. Які можна выкарыстоўваць indexed view in its execution plan even if it does no satisfy these GROUP BY абмежаванні.
  • If the view definition contains a GROUP BY clause, the key of the unique clustered index can reference only the columns specified in the GROUP BY пункт.

Тут бачна, што індусаў не прыцягвалі, бо яны вырашылі рабіць па схеме "зробім мала, але добра". Гэта значыць, у іх мін на полі пабольш, але іх размяшчэнне празрысцей. Больш за ўсё засмучае вось гэтае абмежаванне:

View must reference only base tables які ў той жа самыя 데이터베이스 як view. view cannot reference іншых views.

У нашай тэрміналогіі гэта азначае, што функцыя не можа звяртацца да іншай матэрыялізаванай функцыі. Гэта сячэ ўсю ідэалогію на пні.
Таксама вось гэтае абмежаванне (і далей па тэксце) вельмі моцна памяншае варыянты выкарыстання:

SELECT statement in view definition not contain the following Transact-SQL elements:

COUNT
ROWSET functions (OPENDATASOURCE, OPENQUERY, OPENROWSETІ OPENXML)
OUTER joins (LEFT, RIGHTабо FULL)

Derived table (defined by specifying a SELECT заяву ў FROM clause)
Self-joins
Specifying columns by using SELECT * or SELECT <table_name>.*

DISTINCT
STDEV, STDEVP, VAR, VARPабо AVG
Common table expression (CTE)

плаваць1, тэкст, ntext, малюнак, XMLабо filestream слупкоў
Падзапыт
OVER clause, which includes ranking or aggregate window functions

Full-text predicates (CONTAINS, FREETEXT)
SUM function that references a nullable expresion
ORDER BY

CLR user-defined aggregate function
TOP
CUBE, ROLLUPабо GROUPING SETS аператары

MIN, MAX
UNION, EXCEPTабо INTERSECT аператары
TABLESAMPLE

Table variables
OUTER APPLY or CROSS APPLY
PIVOT, UNPIVOT

Sparse column sets
Inline (TVF) або мульты-статэмент table-valued functions (MSTVF)
OFFSET

CHECKSUM_AGG

Забаронены OUTER JOINS, UNION, ORDER BY і іншыя. Магчыма прасцей было ўказаць, што можна выкарыстоўваць, чым тое, што нельга. Спіс, верагодна, быў бы значна меншы.

Падводзячы вынік: велізарны набор абмежаванняў у кожнай (заўважу камерцыйнай) СКБД vs ніякіх (за выключэннем аднаго лагічнага, а не тэхнічнага) у LGPL тэхналогіі. Аднак варта адзначыць, што рэалізаваць гэты механізм у рэляцыйнай логіцы некалькі складаней, чым у апісанай функцыянальнай.

Рэалізацыя

Як гэта працуе? У якасці "віртуальнай машыны" выкарыстоўваецца PostgreSQL. Унутры ёсць складаны алгарытм, які займаецца пабудовай запытаў. Вось зыходны код. І тамака не проста вялікі набор эўрыстык з кучай if'ов. Так што калі ёсць пару месяцаў на вывучэнне, то можаце паспрабаваць разабрацца ў архітэктуры.

Ці працуе гэта эфектыўна? Досыць эфектыўна. Нажаль, давесці гэта цяжка. Магу толькі сказаць, што калі разгледзець тысячы запытаў, якія ёсць у вялікіх дадатках, то ў сярэднім яны больш эфектыўна, чым у добрага распрацоўшчыка. Выдатны SQL-праграміст можа напісаць любы запыт больш эфектыўна, але на тысячы запытаў у яго проста не будзе ні матывацыі, ні часу гэта рабіць. Адзінае, што я магу зараз прывесці як доказ эфектыўнасці - гэта тое, што на базе платформы, пабудаванай на гэтай СКБД працуюць некалькі праектаў ERP-сістэмы, У якіх ёсць тысячы розных MATERIALIZED функцый, з тысячай карыстальнікаў і террабайтнымі базамі з сотнямі мільёнаў запісаў, якія працуюць на звычайным двух-працэсарным серверы. Зрэшты, любы жадаючы можа праверыць/аспрэчыць эфектыўнасць, запампаваўшы платформа і PostgreSQL, уключаны лагіраванне SQL-запытаў і паспрабаваўшы змяняць там логіку і дадзеныя.

У наступных артыкулах, я таксама раскажу пра тое, як можна вешаць абмежаванні на функцыі, працу з сесіямі зменаў і многае іншае.

Крыніца: habr.com

Дадаць каментар