Як зобов’язати СУБД застосовувати до запитів конкретні прийнятні плани, Інші СУБД, Бази даних, статті




А мої ти куряни – сведомі кметі: под трубами повиті, под шоломи в’злелеяни, конець копія в’скр’млені; шляху імь відома, яруги імь знаємо, луці у них напруги, тули відчинення, шаблі із’стрені; самі скачють, акьі сірки вл’ці в поле, шукаючи собі шануй, а князю слави.
















Слово о полку Ігоревім


Реферат


Розглядається система управління планами (SPM), введена у версії Oracle 11 стосовно повторюваним запитам програми. Вона дозволяє формувати і зберігати для запитів допустимі набори планів (Baselines), змусити СУБД працювати тільки по них і тим самим уникнути в окремих випадках непередбаченого падіння продуктивності при обробці.


Введення


Як відомо, СУБД, отримавши від програми запит, спочатку будує програму обчислення відповіді (“план”), і тут же цю програму відпрацьовує. Теоретично план можна побудувати найкращим чином: отримати множина всіх можливих для даного запиту планів і вибрати з них найбільш поізводітельний в потрібному відношенні. На практиці ж будь-яка промислова СУБД, включаючи Oracle, виробляє план на швидку руку, досягаючи при цьому прийнятного часу відповіді, але зате жертвуючи точністю виконання завдання. Як наслідок застосовуються до вступників запитам плани нерідко виявляються не найкращими, і загальна продуктивність СУБД страждає.


Для вирішення цієї проблеми фірма Oracle, так само як і інші розробники, дає користувачеві кошти втручатися в вироблення плану СУБД. Непряме втручання досягається впливом на фактори, що впливають на вироблення оптимізатором планів: параметри СУБД і сеансу, показники статистики об’єктів запиту, вживання підказок. Пряме втручання полягає в диктуванню СУБД, які саме плани слід використовувати для конкретних запитів.


Історично першим інструментом останньої категорії стали “обриси” (outline) запитів, що з’явилися у версії 8.1. “Обрис” запиту – це план, “схоплений” в підходящий момент часу і збережений для подальшого вживання. Користувач отримує можливість в межах конкретних сеансів “включати” “обрис”, змушуючи СУБД користуватися цим конкретним планом незалежно від поточних обставин. Останньою версією, де ця техніка фіксації плану підтримується, офіційно вважається Oracle 11.


Другим за часом інструментом став “профіль” (profile) запиту, введений у версії 10. Профілі можуть виникнути в результаті спеціальної процедури аналізу СУБД запиту, що представляє інтерес, в рамках роботи радника SQL Tuning Advisor. Користувач має право в будь-який момент включити наявний профіль, і тоді поточний план запиту підкоригувати в бік поліпшення. Впливом на застосовуваний СУБД план буде включення і відключення профілю в міру необхідності.


У версії 11 в Oracle з’явилося третє за рахунком засіб вказівки СУБД, яким опрпделенним планом їй скористатися при обробці того чи іншого запиту. Воно отримало назву “управління планами”: SQL plan management (SPM). Його основне призначення – не дати можливість пустити обробку повторюваних запитів програми по “поганого” плану. “Поганим” же план може виявитися ненароком при зміні обставин чергового надходження запиту – значень змінних прив’язки, статистики об’єктів, змінних СУБД або сеансу і навіть версії СУБД або ОС.


Система управління планами запитів


SPM дозволяє формувати “базу управління запитами” (SQL management base, SMB). Вона розташовується в AWR (automatic workload repository) і може поповнюватися і вичищати як вручну, так і автоматикою AWR. В SMB для кожного представляє інтерес запиту можна накопичувати історію його планів (plan history), а з неї формувати основну лінію (baseline) планів. Суть “основної лінії” в тому, адміністратор формує її з планів, які вважає (“призначає”) задовільно продуктивними. Управління планами обробки на ділі почнеться при перекладі оптимізатора CBO в спеціальний режим “обліку SPM” (SPM aware optimizer). Тоді при будь зміні обставин запуску запиту (або всього додатка) оптимізатор не застосує до нього план, гірше наявних в “основний лінії”.


Режим обліку SPM (використання основних ліній планів) встановлюється значенням TRUE параметра СУБД OPTIMIZER_USE_SQL_PLAN_BASELINES. Це значення умолчательной. Змінити його на FALSE або назад можна як на рівні СУБД, так і окремих сеансів.


При надходженні в СУБД запиту, для нього виробляється план. Далі, якщо OPTIMIZER_USE_SQL_PLAN_BASELINES = FALSE, запит виконується за цим планом. Якщо = TRUE і план присутній і в історії планів, і в основній лінії, він також приймається до виконання. Якщо ж план відсутня або в історії, або в основний лінії, для виконання запиту буде обраний найбільш “легкий” з наявних в основний лінії. Але якщо план відсутній в історії, він додатково буде туди занесений.


Вміст SMB представлено в таблиці DBA_SQL_PLAN_BASELINES. (На ділі це, звичайно, віртуальна таблиця, тобто view із запитом, пред’являє дані з реальних таблиць SQLOBJ $, SQLOBJ $ AUXDATA і SQL $ TEXT схеми SYS). Ці дані загальносистемні, а тому аналогічних таблиць з префіксами USER і ALL в назвах не існує. Ось деякі поля цієї таблиці:



































Поле


Значення


SIGNATURE


“Підпис” запиту, обчислюється по нормалізованому тексту запиту (див. функцію DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE).


SQL_TEXT


Текст запиту.


SQL_HANDLE


Символьне вираз підпису, ключ для зручності пошуку планів основної лінії.


PLAN_NAME


Символьне вираз для позначення плану.


ENABLED


Ознака знаходження плану в робочому стані. Якщо встановити = “NO”, оптимізатор буде цей план ігнорувати.


ACCEPTED


Ознака того, що план включений в основну лінію як прийнятний.


FIXED


Якщо в основній лінії є плани, помічені FIXED = “YES”, вважається, що основна лінія для запиту не підлягає автоматичній перебудові, тобто є фіксованою.


AUTOPURGE


Ознака, що дозволяє автоматичне видалення плану із SBM автоматикою AWR після встановленого часу.


OPTIMIZER_COST, EXECUTIONS, CPU_TIME та ін


Загальні кількісні показники плану.


Крім цього відомості про основну лінії планів для запиту надає у текстовому вигляді функція DISPLAY_SQL_PLAN_BASELINE з пакету DBMS_XPLAN.


Вчинення дій з SPM служить пакет DBMS_SPM. Цей же пакет використовується в OEM для графічного доступу до своєї власної функціональності.


Далі SPM розглядається на прикладі вживання безпосередньо програмним чином.


Підготовка до прикладу


Далі наводиться приклад, де в обставини видачі додатком запиту вносяться зміни у вигляді доданого індексу. Покладемо, ми не дуже впевнені, як це відіб’ється на обробці запиту. Щоб не прорахуватися з непередбаченою втратою ефективності, побудуємо для нього основну лінію, що включає колишній перевірений план. Таким чином після додавання в БД індексу запит свідомо не погіршить продуктивності, але можливо поліпшить.


Для прикладу підібраний нереально простий запит. Це зроблено свідомо воим дохідливості викладу техніки.


У прикладі будуть перемикання в схеми SCOTT і SYS, але передбачається, що робота виконується в SQL * Plus без виходу з цієї програми, що дозволить зберегти значення змінних.


Очистимо для запобігання плутанини загальну область курсорів в shared pool (технічно це необов’язково, але спростить тут звернення до потрібних даних в shared pool), заведемо робочі змінні і скинемо заради простоти показу в файл текст для видачі плану останнього запиту:


CONNECT / AS SYSDBA


ALTER SYSTEM FLUSH SHARED_POOL;


VARIABLE retcode   NUMBER


VARIABLE sqltext   VARCHAR2 ( 1000 )


VARIABLE sqlhandle VARCHAR2 ( 30 )


VARIABLE sqlid     VARCHAR2 ( 13 )


VARIABLE report    CLOB


SELECT * FROM TABLE ( DBMS_XPLAN.DISPLAY_CURSOR ( format => “basic” ) ).


SAVE showplan REPLACE


Передбачається, що основна лінія запитів спочатку порожня. Початково план запиту не залежить від того, застосовує оптимізатор управління планами, чи ні:


SQL> CONNECT scott/tiger


Connected.


SCOTT> SELECT job FROM emp WHERE ename = “MILLER”;


JOB


———


CLERK


SQL> @showplan


PLAN_TABLE_OUTPUT


——————————————————————–


EXPLAINED SQL STATEMENT:


————————


SELECT job FROM emp WHERE ename = “MILLER”


Plan hash value: 3956160932


———————————-


/ Id  / Operation         / Name /


———————————-


/   0 / SELECT STATEMENT  /      /


/   1 /  TABLE ACCESS FULL/ EMP  /


———————————-


13 rows selected.


SQL> ALTER SESSION SET optimizer_use_sql_plan_baselines = FALSE;


Session altered.


SQL> SELECT job FROM emp WHERE ename = “MILLER”;


JOB


———


CLERK


SQL> @showplan


PLAN_TABLE_OUTPUT


——————————————————————–


EXPLAINED SQL STATEMENT:


————————


SELECT job FROM emp WHERE ename = “MILLER”


Plan hash value: 3956160932


———————————-


/ Id  / Operation         / Name /


———————————-


/   0 / SELECT STATEMENT  /      /


/   1 /  TABLE ACCESS FULL/ EMP  /


———————————-


Завантаження плану в базу управління запитами


Ось випадок, гідний пам’яті і занесення в літературу!


Пліній Молодший, Панегірик імператору Траяну


Зараз для нас цікавить запиту СУБД завела робочу пам’ять в загальній області курсорів в shared pool. Завантажимо звідти план (перший по рахунку) в основну лінію в базі управління запитами SMB, пославшись на ідентифікатор курсора SQL ID:


CONNECT / AS SYSDBA


EXECUTE :sqltext := q”[SELECT job FROM emp WHERE ename = “MILLER”]”


EXECUTE –


 SELECT sql_id INTO :sqlid FROM v$sqlarea WHERE sql_text = :sqltext


EXECUTE :retcode := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE ( :sqlid )


Перевірка:


SQL> SELECT :retcode “Plans selected:” FROM dual;


Plans selected:


—————


              1


Для простоти подальших звернень до таблиці DBA_SQL_PLAN_BASELINES за відомостями про SMB запам’ятаємо у файлі ще один робочий запит. Він параметризовані ключем прикладного запиту, який дізнаємося з тексту запиту і помістимо в змінну SQLHANDLE:


COLUMN sql_text FORMAT A15 WRAP


COLUMN enabled  FORMAT A10


COLUMN accepted FORMAT A10


SELECT


   plan_name, sql_text, enabled, accepted


FROM


   dba_sql_plan_baselines


WHERE sql_handle = &1


.


SAVE baseline REPLACE


SET VERIFY OFF


BEGIN


 SELECT DISTINCT sql_handle INTO :sqlhandle


 FROM   dba_sql_plan_baselines


 WHERE  DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE ( :sqltext ) = signature;


END;


/


Перевірка:


SQL> PRINT sqlhandle


SQLHANDLE


——————————–


SYS_SQL_dd7adbcd38c100c0


SQL> @baseline :sqlhandle


PLAN_NAME                      SQL_TEXT        ENABLED    ACCEPTED


—————————— ————— ———- ———-


SYS_SQL_PLAN_38c100c0d8a279cc  SELECT job FROM YES        YES


                                emp WHERE enam


                               e = “MILLER”


Незважаючи на те, що в нашому випадку запит потрапив в SMB за посиланням на SQL ID, в самій базі він ідентифікується ключем SQL_HANDLE, який автоматично породжується по підпису запиту, в свою чергу обчислюється по нормалізованності тексту. Це дозволяє зберігати план в AWR довготривало, незалежно від того, представлений чи запит взагалі в курсорною області в даний момент, і під яким саме SQL ID представлений.


Зверніть увагу, що використаний спосіб завантаження плану в основну лонію автоматично виставив ознаки ENABLED і ACCEPTED в стан “YES”, то є єдиний поки план в SMB і в робочому стані, і включений в основну лінію.


Використання основної лінії планів запиту оптимізатором CBO


Змінимо обставини запуску запиту, індексувати таблицю. План повинен помінятися. Однак при включеному (за замовчуванням) управлінні планами ми цього не побачимо. Примітно, що переконатися в цьому вдасться тільки з другої спроби:


SQL> CONNECT scott/tiger


Connected.


SQL> create index emp_ename on emp ( ename );


Index created.


SQL> SELECT job FROM emp WHERE ename = “MILLER”;


JOB


———


CLERK


SQL> @showplan


PLAN_TABLE_OUTPUT


——————————————————————–


SQL_ID  a7zgruuhu1nkf, child number 3


An uncaught error happened in prepare_sql_statement : ORA-01403: no data found


NOTE: cannot fetch plan for SQL_ID: a7zgruuhu1nkf, CHILD_NUMBER: 3


      Please verify value of SQL_ID and CHILD_NUMBER;


      It could also be that the plan is no longer in cursor cache (check v$sql_plan)


8 rows selected.


SQL> SELECT job FROM emp WHERE ename = “MILLER”;


JOB


———


CLERK


SQL> @showplan


PLAN_TABLE_OUTPUT


——————————————————————–


EXPLAINED SQL STATEMENT:


————————


SELECT job FROM emp WHERE ename = “MILLER”


Plan hash value: 3956160932


———————————-


/ Id  / Operation         / Name /


———————————-


/   0 / SELECT STATEMENT  /      /


/   1 /  TABLE ACCESS FULL/ EMP  /


———————————-


Перший раз оптимізатор побудував новий план, з урахуванням індексу, але в SMB його не виявилося. Тоді оптимізатор заніс план в історію і виконав запит по єдиному в основний лінії планом – старому. З другого разу робоча область в shared pool виявилася заведена, але запит і раніше був відпрацьований по єдиному в основний лінії старим планом. Якщо ж управління планами відключити, СУБД відпрацює за більш вигідним в цій версії оптимізатора новим планом:


SQL> ALTER SESSION SET optimizer_use_sql_plan_baselines = FALSE;


Session altered.


SQL> SELECT job FROM emp WHERE ename = “MILLER”;


JOB


———


CLERK


SQL> @showplan


PLAN_TABLE_OUTPUT


——————————————————————


EXPLAINED SQL STATEMENT:


————————


SELECT job FROM emp WHERE ename = “MILLER”


Plan hash value: 106684950


————————————————-


/ Id  / Operation                   / Name      /


————————————————-


/   0 / SELECT STATEMENT            /           /


/   1 /  TABLE ACCESS BY INDEX ROWID/ EMP       /


/   2 /   INDEX RANGE SCAN          / EMP_ENAME /


————————————————-


Поповнення основної лінії планів шляхом оцінки планів


За результатом виконаних дій в основний лінії планів для нашого запиту виявилося два плани: з урахуванням індексу (ознака ACCEPTED = “NO”) і без обліку (ознака ACCEPTED = “YES”):


SQL> CONNECT / AS SYSDBA


SQL> @baseline :sqlhandle


PLAN_NAME                      SQL_TEXT        ENABLED    ACCEPTED


—————————— ————— ———- ———-


SYS_SQL_PLAN_38c100c08916fd8c  SELECT job FROM YES        NO


                                emp WHERE enam


                               e = “MILLER”


SYS_SQL_PLAN_38c100c0d8a279cc  SELECT job FROM YES        YES


                                emp WHERE enam


                               e = “MILLER”


Можна видати оптимізатору завдання перевірити за плани ознаками ACCEPTED = “NO” (тобто враховані в SMB, але не зараховані до прийнятних) на ефективність і включити їх в основну лінію (позначити як “прийнятні”), якщо вони виявляться не гірше раніше там були:


BEGIN


 SELECT DISTINCT sql_handle INTO :sqlhandle


 FROM   dba_sql_plan_baselines


 WHERE  DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE ( :sqltext ) = signature;


END;


/


EXECUTE :report :=                                               –


    DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE ( sql_handle => :sqlhandle )


Першу перевірку змін до SMB виконаємо за ознакою ACCEPTED в довідковій таблиці:


SQL> @baseline :sqlhandle


PLAN_NAME                      SQL_TEXT        ENABLED    ACCEPTED


—————————— ————— ———- ———-


SYS_SQL_PLAN_38c100c08916fd8c  SELECT job FROM YES        YES


                                emp WHERE enam


                               e = “MILLER”


SYS_SQL_PLAN_38c100c0d8a279cc  SELECT job FROM YES        YES


                                emp WHERE enam


                               e = “MILLER”


Другу перевірку виконаємо по вмісту змінної REPORT, складеним функцією EVOLVE_SQL_PLAN_BASELINE:


SQL> SET LONG 10000


SQL> SELECT :report “Baseline evolution results:” FROM dual;


Baseline evolution results:


———————————————————————


———————————————————————


                        Evolve SQL Plan Baseline Report


———————————————————————


Inputs:


——-


  SQL_HANDLE = SYS_SQL_dd7adbcd38c100c0

  PLAN_NAME  =


  TIME_LIMIT = DBMS_SPM.AUTO_LIMIT


  VERIFY     = YES


  COMMIT     = YES


Plan: SYS_SQL_PLAN_38c100c08916fd8c


———————————–


  Plan was verified: Time used .06 seconds.


  Passed performance criterion: Compound improvement ratio >= 1.5.


  Plan was changed to an accepted plan.


                      Baseline Plan      Test Plan     Improv. Ratio


                      ————-      ———     ————-


  Execution Status:        COMPLETE       COMPLETE


  Rows Processed:                 1              1


  Elapsed Time(ms):               0              0


  CPU Time(ms):                   0              0


  Buffer Gets:                    3              2               1.5


  Disk Reads:                     0              0


  Direct Writes:                  0              0


  Fetches:                        0              0


  Executions:                     1              1


———————————————————————


                                 Report Summary


———————————————————————


Number of SQL plan baselines verified: 1.


Number of SQL plan baselines evolved: 1.


Вердикт про включенні плану в основну лінію здійснюється на підставі порівняння зважених сумарних оцінок з перелічених у звіті показниками (“Compound improvement ratio”; точне правило не оголошується). У нашому випадку він виявився сприятливим для плану-кандидата (коефіцієнт поліпшення ефективності> = 1.5).


Тепер можна перевірити колишнім манером, як поява другого плану в основний лінії відбилося на відпрацюванні запиту, видавши послідовність:


CONNECT scott/tiger


SELECT job FROM emp WHERE ename = “MILLER”;


@showplan


ALTER SESSION SET optimizer_use_sql_plan_baselines = FALSE;


SELECT job FROM emp WHERE ename = “MILLER”;


@showplan


На цей раз плани виявляться однаковими, “новими” (INDEX RANGE SCAN ® TABLE ACCESS BY INDEX ROWID).


Інші способи управління основною лінією планів


Поповнення і ручна чистка основної лінії планів


Основну лінію планів якогось запиту можна поповнювати (“розвивати”, evolve): вручну або автоматично.


Ручне поповнення основної лінії в результаті запуску завдання на перевірку прийнятності плану виконується функцією EVOLVE_SQL_PLAN_BASELINE і демонструвалося вище.


Процедура LOAD_PLANS_FROM_SQLSET дозволяє завантажувати основну лінію плани з настроечного набору (SQL tuning set). Настроювальний набір може бути отриманий будь-яким доступним шляхом, наприклад перенесений з іншої БД, можливо навіть з версії 10.


Процедури PACK_STGTAB_BASELINE і UNPACK_STGTAB_BASELINE дозволяють зберегти плани основних ліній у спеціально створеної таблиці і завантажувати їх з такої таблиці.


Включення в сеансі параметра СУБД OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES в стан TRUE викличе автоматичне поповнення SMB планами запитів, що надходять з програми. Для запитів програми, основні лінії планів яких бажано виключити з процедури автоматичного поповнення (тобто “зафіксувати”), можна використовувати значення атрибута FIXED = “YES” планів, що складають відповідну лінію. Наявність планів з атрибутом FIXED = “YES” перешкоджає тільки автоматичного поповнення і не позначається на можливості додавати плани вручну, по SQL ID і по настроювальному набору.


Автоматичне поповнення основних ліній також може здійснюватися в результаті “схвалення” (прийняття) адміністратором профілю, рекомендованого для запиту радником SQL Tuning Advisor. За замовчуванням цей радник запускається автоматичним завданням в “віконце підтримки” СУБД щодоби.


Шлях потрапляння плану в основну лінію позначений в таблиці DBA_SQL_PLAN_BASELINES в поле ORIGIN:


SQL> SELECT


  2     sql_handle, plan_name, origin


  3  FROM


  4     dba_sql_plan_baselines


SQL> ;


SQL_HANDLE               PLAN_NAME                     ORIGIN


———————— —————————– ————


SYS_SQL_dd7adbcd38c100c0 SYS_SQL_PLAN_38c100c08916fd8c AUTO-CAPTURE


SYS_SQL_dd7adbcd38c100c0 SYS_SQL_PLAN_38c100c0d8a279cc MANUAL-LOAD


Ручне видалення плану з основної лінії виконується функцією DROP_SQL_PLAN_BASELINE.


Зміна властивостей планів в SMB


Процедура ALTER_SQL_PLAN_BASELINE дозволяє встановлювати атрибутам ENABLED, FIXED і AUTOPURGE плану необхідні значення явочним порядком. Приклад:


EXECUTE :retcode := DBMS_SPM.ALTER_SQL_PLAN_BASELINE               –


    ( :sqlhandle, “SYS_SQL_PLAN_38c100c08916fd8c”, “enabled”, “no” )


SELECT :retcode “Plans disabled:” FROM dual;


@baseline :sqlhandle


Тепер система управління планами знову відмовиться застосовувати “новий” план до запиту, не дивлячись на його присутність в основній лінії планів – завдяки неробочому стану.


Регулювання накопичення і зберігання планів в основних лініях


Технічно основні лінії планів у складі SMB зберігаються в AWR в табличному просторі SYSAUX, і регламент їх зберігання визначається внутрішніми перевірками і автоматикою AWR. Цей регламент задається наступними характеристиками:


– Максимальним відсотком даних SMB в просторі SYSAUX (від 1 до 50%, від початку 10%);


– Максимальним періодом відсутності інтересу до плану, після чого він буде автоматично видалений (від 5 до 523 тижнів, початково 53).


Дізнатися поточні характеристики регламенту накопичення і зберігання SMB можна запитом:


SELECT


   parameter_name, parameter_value


FROM


   dba_sql_management_config


;


Змінити ці характеристики можна процедурою CONFIGURE, наприклад:


EXECUTE DBMS_SPM.CONFIGURE (                 –


   parameter_name  => “space_budget_percent” –


,  parameter_value => 20                     )


Уроки системи управління планами в Oracle


Система управління планами в Oracle здатна не тільки зберегти продуктивність при зміні обставин запуску запитів, але і дати користувачеві уроки.


Розглянемо план для запиту SELECT DISTINCT … (далі все аналогічно для запитів з UNION і GROUP BY). Як відомо, у версіях до 9 включно цей запит обробляється із застосуванням внутрішнього сортування SORT UNIQUE. З версії 10 оптимізатор пропонує для такого запиту план з HASH UNIQUE, внутрішньої процедурою розстановки рядків, з “хешування”. Більшість користувачів, що звернули на це увагу, порахували його доцільним нововведенням, поліпшує продуктивність відпрацювання. Проте спроба застосувати для таких запитів управління планами (хоча б заради збереження продуктивності) змушує в цьому засумніватися.


Дійсно, спробуємо в стилі вищевикладеного побудувати основну лінію планів для іншого відправного випадку та іншого запиту. Видамо:


ALTER SESSION SET optimizer_features_enable = “9.2.0”;


ALTER SESSION SET optimizer_mode = ALL_ROWS;


SELECT DISTINCT job FROM emp;


Включимо план в основну лінію, як вище. Це буде план з SORT UNIQUE. Поміняємо обставини видачі запиту, наприклад:


ALTER SESSION SET optimizer_features_enable = “11.1.0.6.1”;


SELECT DISTINCT job FROM emp;


Вийде план з HASH UNIQUE. Проте спроба доповнити їм основну лінію планів запиту функцією EVOLVE_SQL_PLAN_BASELINE приречена. SPM не вважає новий план, який дають версії 10 + для цього запиту, краще колишнього!


Більш пильна вивчення виявляє, що план з HASH UNIQUE має б о більшу вартість обробки (cost), ніж “старий” з SORT UNIQUE (10 одиниць проти 5), хоча з ростом розміру таблиці цей програш і скорочується.


Я не знайшов пояснення цьому явищу, однак якщо тут немає підводних каменів, система управління планами здатна і в цьому випадку запобігти несподваний небажаний зростання витрат на обробку.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*