Oracle mechanics

27.10.2012

Long parse и фиксация плана выполнения

Filed under: CBO,heuristics,Oracle,Plan Management — Игорь Усольцев @ 19:51
Tags: , , ,

В очередной раз столкнувшись с ранее описанной проблемой типа Медленный разбор SQL запроса (long parse time): вариант №2, решил протестировать, в какой степени могут быть полезны для уменьшения времени разбора (hard parse) штатные методы фиксации / модификации планов выполнения, предлагаемые Oracle:

  • относительно новый механизм SQL Plan Management (SPM) в лице baseline
  • косный «устаревший» outline, появившийся в версии 8.1
  • sql patch, также являющегося элементом SPM

Первое выполнение оригинального запроса происходит медленно:

11.2.0.3.OEBS@ SQL> SELECT
  2  *
  3  FROM SOME_COMP_VIEW
  4  WHERE currency_code = 'RUB'
  5  and vat_type = '10'
  6  order by to_number(r_type), trx_number;

no rows selected

Elapsed: 00:00:27.33

SQL> /

no rows selected

Elapsed: 00:00:00.01 -- 2-е выполнение - быстрое, что может быть коственным признаком длительного разбора

Статистика первого выполнения однозначно указывает на длительный hard parse:

SQL> @sessof 2320

Session Time Model

STAT_NAME                                                               DELTA
---------------------------------------------------------------- ------------
DB time                                                              27334680
parse time elapsed                                                   27281348
hard parse elapsed time                                              27261801
DB CPU                                                               27255856
...

Session Statistics

NAME                                                                    DELTA
---------------------------------------------------------------- ------------
...
recursive calls                                                          9043
...
DB time                                                                  2734
parse time elapsed                                                       2731
CPU used by this session                                                 2725
CPU used when call started                                               2724
parse time cpu                                                           2723
recursive cpu usage                                                      2720
...
parse count (total)                                                       952 -- # of parse calls (hard, soft, and describe)
...
parse count (hard)                                                          1

Session Wait Events                                                           -- ожиданий практически нет

NAME                      WAITS      TIME_MS     TIMEOUTS AVG_WAIT_MS
------------------ ------------ ------------ ------------ -----------
library cache lock           46           11            0         0,2
library cache pin            44           11            0         0,2

Причиной длительного разбора по-прежнему является большое количество используемых таблиц + громоздкая / неоптимальная конструкция запроса с использованием множественных вложенных параметризованных обзоров:

SQL> select count(*)
  2    from v$sql_plan
  3   where sql_id = '&&sql_id'
  4     and child_number = 1
  5     and operation = 'TABLE ACCESS'
  6  /

  COUNT(*)
----------
       166

Судя по 10053 трейсу (размером > 240 MB) при подготовке плана оптимизатор достаточно долго тестирует более 1400 комбинаций перестановок таблиц:

$ ls -ltr | grep ORIG
-rw-r----- 1 oracle oinstall  254794934 Oct  8 19:08 OEBS_ora_9588_ORIGINAL.trc

$ grep  'Number of join permutations tried' OEBS_ora_9588_ORIGINAL.trc |  awk '{print $6}' | sort -n | tail -n1
1403

Описанный ранее метод прямого отключения трансформаций и ограничения количества пермутаций через подсказки:

SQL> SELECT /*+ NO_QUERY_TRANSFORMATION
  2             opt_param( '_optimizer_cost_based_transformation' 'off' )
  3             opt_param( '_optimizer_max_permutations' 10) */
  4  *
  5  FROM SOME_COMP_VIEW
  6  WHERE currency_code = 'RUB'
  7  and vat_type = '10'
  8  order by to_number(r_type), trx_number;

no rows selected

Elapsed: 00:00:02.93

— уменьшает общее время разбора / первого выполнения запроса / размер трейс файла приблизительно на порядок:

$ ls -ltr | grep HINT
-rw-r----- 1 oracle oinstall   30624265 Oct  8 19:23 OEBS_ora_19269_HINTED.trc

$ grep  'Number of join permutations tried' OEBS_ora_19269_HINTED.trc |  awk '{print $6}' | sort -n | tail -n1
95 ### всего 95 пермутаций, дальнейшее уменьшение _optimizer_max_permutations уже не влияет на это количество для запроса из 166 таблиц

, статистика выпонения также выглядит значительно лучше:

SQL> @sessof 2320

Session Time Model

STAT_NAME                                                        DELTA
---------------------------------------------------------------- ------------
DB time                                                          2892492
parse time elapsed                                               2851925
hard parse elapsed time                                          2843055
DB CPU                                                           2821571

SPM Baseline

Создание фиксированного baseline лишь незначительно сокращает время hard parse:

SQL> declare res number;
  2  begin
  3    res := dbms_spm.load_plans_from_cursor_cache(sql_id => 'g02fdpz2yqkj1', plan_hash_value => '1733923699',fixed => 'yes');
  4  end;
  5  /

PL/SQL procedure successfully completed.

SQL> @purge_cu g02fdpz2yqkj1                              -- удаление курсора из shared pool

SQL> select 1 from gv$sql where sql_id = 'g02fdpz2yqkj1'; -- контрольный

no rows selected

SQL> SELECT --4BASELINE
  2   *
  3    FROM SOME_COMP_VIEW
  4   WHERE currency_code = 'RUB'
  5     and vat_type = '10'
  6   order by to_number(r_type), trx_number
  7  /

no rows selected

Elapsed: 00:00:23.81                           -- неожиданно большое время hard parse для запроса с подготовленным планом

SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor('','','all -alias -projection -predicate last'));

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  g02fdpz2yqkj1, child number 1
-------------------------------------
...

Plan hash value: 1733923699
...
Note
-----
   - SQL plan baseline SQL_PLAN_bcj4t7s4w1zc2684e2559 used for this statement

SQL> select hash_value, child_number, plan_hash_value, sql_plan_baseline, outline_category from v$sql where sql_id = 'g02fdpz2yqkj1';

HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE SQL_PLAN_BASELINE              OUTLINE_CATEGORY
---------- ------------ --------------- ------------------------------ ----------------
3320531489            1      1733923699 SQL_PLAN_bcj4t7s4w1zc2684e2559

1 row selected.

Обращает внимание использование child=1 для первого выполнения запроса. При наличии baseline для запроса, отсутствующего в library cache, всякий раз будет выполняться «тестовая» генерация нового курсора (child=0), но использоваться будет курсор с планом, сохранённом в baseline:

SQL> select c.type,
  2         c.namespace,
  3         c.sharable_mem,
  4         c.loads,
  5         c.executions,
  6         c.invalidations,
  7         c.status,
  8         c.timestamp,
  9         c.locked_total,
 10         c.pinned_total
 11    from v$db_object_cache c
 12   where c.name = (select sql_text from v$sqlarea where sql_id = 'g02fdpz2yqkj1')
 13  /

TYPE   NAMESPACE SHARABLE_MEM LOADS EXECUTIONS INVALIDATIONS STATUS TIMESTAMP           LOCKED_TOTAL PINNED_TOTAL
------ --------- ------------ ----- ---------- ------------- ------ ------------------- ------------ ------------
CURSOR SQL AREA             0     1          0             1 UNKOWN 2012-10-25/18:36:51            1            2 -- Child#0 инвалидирован
CURSOR SQL AREA       3756496     1          1             0 VALID  2012-10-25/18:36:51            1            2 -- Child#1 выполнялся
CURSOR SQL AREA          4832     3          1             1 VALID  2012-10-25/18:36:51            1            1 -- Parent Cursor

3 rows selected.

— дочерний курсор #0 был успешно  создан и инвалидирован, в V$SQL не отражается

Из  10053 трейса также следует, что использование baseline незначительно сокращает длину файла/время выполнения, не уменьшая при этом количества испытываемых перестановок таблиц, т.е. производит почти полный разбор для создания нового плана выполнения курсора #0:

$ ls -ltr OEBS_ora_5840_BASELINE.trc
-rw-r----- 1 oracle oinstall 183448283 Oct 21 22:29 OEBS_ora_5840_BASELINE.trc

$ grep  'Number of join permutations tried' OEBS_ora_5840_BASELINE.trc |  awk '{print $6}' | sort -n | tail -n1
1403

Несмотря на то, что запрос был обнаружен в SPM в первых строках трейса:

$ less -N OEBS_ora_5840_BASELINE.trc
   1661 SPM: statement found in SMB                                          ### запрос найден в SPM
...                                                                          ### парсинг курсора #0
3809032 SPM: cost-based plan found in the plan baseline, planId = 1749951833 ### получение готового плана курсора #1 из SPM
3809033 SPM: cost-based plan successfully matched, planId = 1749951833
3809034 Starting SQL statement dump
...
3810252 Content of other_xml column
3810253 ===========================
3810254   db_version     : 11.2.0.3
3810255   parse_schema   : APPS
3810256   plan_hash      : 1733923699
3810257   plan_hash_2    : 1749951833
...
3812964 SPM: kkopmCheckSmbUpdate (enter) xscP=0x7f7634038808, pmExCtx=0xcc34b8d0, ciP=0x9d7d6b0d0, dtCtx=0xbb08d00

— Oracle проверяет совпадение baseline с запросом лишь на последнем этапе формирования плана — после формирования нового плана курсора #0

plan_hash      : 1733923699 — стандартный идентификатор плана, с которым выполняется запрос, а plan_hash_2    : 1749951833 — идентикатор плана, хранящегося в SPB:

SQL> select plan_id from sqlobj$ where name = 'SQL_PLAN_bcj4t7s4w1zc2684e2559';

   PLAN_ID
----------
1749951833

Во время компиляции / hard parse запроса, для которого в AWR имеется готовый план выполнения в виде baseline, Oracle всегда выполняет новый разбор запроса, результатом которого могло бы быть автоматическое создание нового пока ещё неодобренного baseline с признаком DBA_SQL_PLAN_BASELINES.ACCEPTED = ‘NO’¹

В описываемом случае ни окружение сессии/ системы, ни объекты бд не менялись и новый baseline создан не был:

SQL> select sql_handle, plan_name, origin
  2    from dba_sql_plan_baselines
  3   where sql_handle = 'SQL_b644993e09c0fd82'
  4  /

SQL_HANDLE                     PLAN_NAME                      ORIGIN
------------------------------ ------------------------------ -----------
SQL_b644993e09c0fd82           SQL_PLAN_bcj4t7s4w1zc2684e2559 MANUAL-LOAD

1 row selected.

Поскольку всякий раз в силу своей эволюционной направленности в процессе полного разборе запроса Oracle будет создать новый план, а использовать для выполнения запроса — сохранённый одобренный план из SPM — для сокращения времени hard parse технологиия baseline подходит слабо

Outline

Если  же создать outline для того же запроса:

SQL> ALTER SESSION SET CREATE_STORED_OUTLINES = LONG_PARSE_OUTL;

Session altered.

SQL> SELECT --4OUTLINE
  2   *
  3    FROM SOME_COMP_VIEW
  4   WHERE currency_code = 'RUB'
  5     and vat_type = '10'
  6   order by to_number(r_type), trx_number
  7  /

no rows selected

SQL> ALTER SESSION SET CREATE_STORED_OUTLINES = FALSE;

Session altered.

SQL> ALTER SESSION SET USE_STORED_OUTLINES = LONG_PARSE_OUTL;

Session altered.

SQL> SELECT --4OUTLINE
  2   *
  3    FROM SOME_COMP_VIEW
  4   WHERE currency_code = 'RUB'
  5     and vat_type = '10'
  6   order by to_number(r_type), trx_number
  7  /

no rows selected

Elapsed: 00:00:04.67                   -- неплохое время разбора: ближе к запросу с подсказками, чем к baseline

SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor('','','all -alias -projection -predicate last'));

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  g02fdpz2yqkj1, child number 2
-------------------------------------
...

Plan hash value: 1733923699           -- естественно, используется тот же план что и в baseline

Note
-----
   - outline "SYS_OUTLINE_12102114075664101" used for this statement

754 rows selected.                    -- план по-прежнему достаточно многострочный

SQL> select name, category, enabled, used from user_outlines;

NAME                           CATEGORY                       ENABLED  USED
------------------------------ ------------------------------ -------- ------
SYS_OUTLINE_12102114075664101  LONG_PARSE_OUTL                ENABLED  USED

— можно видеть, как использование outline значительно сокращает время разбора SQL

Интересно, что при компиляции курсоров используется либо baseline,либо outline:

SQL> select hash_value, child_number, plan_hash_value, sql_plan_baseline, outline_category from v$sql where sql_id = 'g02fdpz2yqkj1';

HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE SQL_PLAN_BASELINE              OUTLINE_CATEGORY
---------- ------------ --------------- ------------------------------ ----------------
3320531489            0      1733923699 SQL_PLAN_bcj4t7s4w1zc2684e2559
3320531489            1      1733923699 SQL_PLAN_bcj4t7s4w1zc2684e2559
3320531489            2      1733923699                                LONG_PARSE_OUTL

10053 трейс показывает, что в присутствии outline использование SPM автоматически блокируется:

$ grep -n SPM OEBS_ora_11294_OUTLINE.trc
1661:SPM: current statement disallowed

Использование outline значительно сокращает размер трейса / время hard parse и количество пермутаций:

$ ls -l OEBS_ora_11294_OUTLINE.trc
-rw-r----- 1 oracle oinstall 17523422 Oct 21 14:13 OEBS_ora_11294_OUTLINE.trc
$ grep  'Number of join permutations tried' OEBS_ora_11294_OUTLINE.trc |  awk '{print $6}' | sort -n | tail -n1
274

Судя по трейсу сокращение числа пермутаций достигается за счёт точного следования подсказкам, составляющим outline, на протяжении всей компиляции плана, исключая любые  противоречащие сохранённому плану трансформации:

$ grep utline OEBS_ora_11294_OUTLINE.trc
JPPD:     JPPD bypassed: Outline does not contain hint.
OJPPD:     OJPPD bypassed: Outline does not contain hint.
...
  outline        : SYS_OUTLINE_12102114075664101
  Outline Data:
...

Подсказки, составляющие планы, зафиксированные в outline и в baseline, естественно полностью совпадают:

SQL> select dbms_lob.substr(hint) as outline_hints from user_outline_hints where name = 'SYS_OUTLINE_12102114075664101'
  2  minus
  3  select substr(extractvalue(value(d), '/hint'), 1, 512) as outline_hints
  4    from xmltable('/outline_data/hint' passing
  5                  (select xmltype(comp_data) as xmlval
  6                     from sys.sqlobj$data od, sys.sqlobj$ o
  7                    where o.obj_type = 2
  8                      and od.obj_type = 2
  9                      and o.name = 'SQL_PLAN_bcj4t7s4w1zc2684e2559'
 10                      and o.signature = od.signature
 11                      and o.plan_id = od.plan_id
 12                      and comp_data is not null)) d
 13  /

no rows selected

SQL Patch

SQL> SELECT --4SQLPatch
  2   *
  3    FROM SOME_COMP_VIEW
  4   WHERE currency_code = 'RUB'
  5     and vat_type = '10'
  6   order by to_number(r_type), trx_number
  7  /

no rows selected

Elapsed: 00:00:25.67

SQL> select sql_id from v$sql where sql_text like 'SELECT --4SQLPatch%';

SQL_ID
-------------
at2zp5rnnd53u                                                            -- целевой sql_id

SQL>                                                                     -- для добавления sql patch
SQL> begin
  2      for reco in (select sql_fulltext from v$sqlarea where sql_id = 'at2zp5rnnd53u')
  3        loop
  4        dbms_sqldiag_internal.i_create_patch(
  5          sql_text => reco.sql_fulltext,
  6          hint_text => 'NO_QUERY_TRANSFORMATION opt_param(''_optimizer_cost_based_transformation'' ''off'') opt_param(''_optimizer_max_permutations'' 10)',
  7          name => 'SHORT_PARSE');
  8        end loop;
  9  end;
 10  /

PL/SQL procedure successfully completed.

SQL> select name,
  2         category,
  3         to_char(created, 'dd.mm.yyyy hh24:mi:ss') as created,
  4         status,
  5         force_matching
  6    from dba_sql_patches
  7  /

NAME        CATEGORY CREATED             STATUS  FORCE_MATCHING
----------- -------- ------------------- ------- --------------
SHORT_PARSE DEFAULT  23.10.2012 17:24:20 ENABLED NO

SQL>                                                                          -- содержимое патча
SQL> select substr(extractvalue(value(d), '/hint'), 1, 512) as sql_patch_hints
  2      from xmltable('/outline_data/hint' passing
  3                    (select xmltype(comp_data) as xmlval
  4                       from sys.sqlobj$data od, sys.sqlobj$ o
  5                      where od.obj_type = 3
  6                        and (o.name = 'SHORT_PARSE' and o.obj_type = 3)
  7                        and o.signature = od.signature
  8                        and comp_data is not null)) d
  9    /

SQL_PATCH_HINTS
---------------------------------------------------------------------------------------------------------------------------
NO_QUERY_TRANSFORMATION opt_param('_optimizer_cost_based_transformation' 'off') opt_param('_optimizer_max_permutations' 10)

SQL> @purge_cu at2zp5rnnd53u                             -- очистка курсора из кэша

PL/SQL procedure successfully completed.

SQL> select 1 from v$sql where sql_id = 'at2zp5rnnd53u'; -- проверка

no rows selected

SQL> SELECT --4SQLPatch
  2   *
  3    FROM SOME_COMP_VIEW
  4   WHERE currency_code = 'RUB'
  5     and vat_type = '10'
  6   order by to_number(r_type), trx_number
  7  /

no rows selected

Elapsed: 00:00:02.86

SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor('','','all -alias -projection -predicate last'));

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  at2zp5rnnd53u, child number 0
-------------------------------------

...

Note
-----
   - SQL patch "SHORT_PARSE" used for this statement

877 rows selected.

SQL> select hash_value, child_number, plan_hash_value, sql_plan_baseline, outline_category, sql_patch from v$sql where sql_id = 'at2zp5rnnd53u';

HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE SQL_PLAN_BASELINE OUTLINE_CATEGORY SQL_PATCH
---------- ------------ --------------- ----------------- ---------------- -----------
3913716858            0      2773864213                                    SHORT_PARSE

1 row selected.

Влияние SQL Patch на время разбора практически эквивалентно применению текстовых подсказок в тексте запроса — очень удобно, учитывая, что при этом изменений в текст запроса вносить не требуется

Трейс оптимизатора подтверждает, что как и в случае с baseline присутствие запроса в SPM обнаруживается в начале компиляции, но сдержащиеся в sql patch подсказки используется с самого начала компиляции плана:

$ ls -l OEBS_ora_32156_SQLPATCH.trc
-rw-r----- 1 oracle oinstall 30372352 Oct 23 17:41 OEBS_ora_32156_SQLPATCH.trc

$ grep  'Number of join permutations tried' OEBS_ora_32156_SQLPATCH.trc |  awk '{print $6}' | sort -n | tail -n1
95 ### всего 95 пермутаций, также как для запроса с тексторыми подсказками

$ less -N OEBS_ora_32156_SQLPATCH.trc
...
  1661 SPM: statement found in SMB
  1662 SPM: current statement has no SQL plan baseline, sig = 389417875499252660 ### = SQLOBJ$.SIGNATURE
...
  2728   ***************************************
  2729   PARAMETERS IN OPT_PARAM HINT
  2730   ****************************
  2731   _optimizer_max_permutations         = 10                   ### изменения параметров оптимизатора из SQL Patch
  2732   _optimizer_cost_based_transformation = 'off'               ### --//--
  2733 ***************************************
...
388508 SPM: statement in SMB but no plan baseline, sig = 389417875499252660
...
389911 Content of other_xml column
389912 ===========================
389913   db_version     : 11.2.0.3
389914   parse_schema   : APPS
389915   plan_hash      : 2773864213
389916   plan_hash_2    : 2848368436
389917   sql_patch      : SHORT_PARSE                                ### ***
389918   Outline Data:
389919   /*+
389920     BEGIN_OUTLINE_DATA
...
389924       OPT_PARAM('_optimizer_max_permutations' 10)             ### те же параметры в OUTLINE-секции
389925       OPT_PARAM('_optimizer_cost_based_transformation' 'off')
...
390796     END_OUTLINE_DATA
390797   */
...

¹) Описание механизма автоматической генерации baseline-ов: Владимир Пржиялковский. Как обязать СУБД применять к запросам конкретные приемлемые планы

В ранее описанном случае Параметризованные обзоры и Cardinality Feedback в Oracle 11.2 после ручного (ORIGIN = MANUAL-LOAD) создания baseline в течение нескольких минут автоматически (ORIGIN = AUTO-CAPTURE) было создано несколько дополнительных baseline-ов, основанием для создания которых вероятнее всего была неудовлетворительная статистика выполнения — Cardinality Feedback

Интересно отметить, что стоимость этих автоматически добавленных планов выше ручного оригинала, что формально противоречит принципам Cost-Based оптимизации :)

SQL> select plan_name,
  2         optimizer_cost,
  3         executions,
  4         origin,
  5         created,
  6         last_executed,
  7         enabled,
  8         accepted,
  9         reproduced
 10    from dba_sql_plan_baselines a
 11   where sql_handle = 'SQL_66a57f45ad0a7744'
 12   order by created
 13  /

PLAN_NAME                      OPTIMIZER_COST EXECUTIONS ORIGIN         CREATED            ENABLED ACCEPTED REPRODUCED
------------------------------ -------------- ---------- -------------- ------------------ ------- -------- ----------
SQL_PLAN_6d9bz8qqhnxu44584d26d          58744         16 MANUAL-LOAD    09-OCT-12 17.05.23 YES     YES      NO
SQL_PLAN_6d9bz8qqhnxu4d6e9c194          93335          0 AUTO-CAPTURE   09-OCT-12 17.08.09 YES     NO       YES
SQL_PLAN_6d9bz8qqhnxu4d776248a          59259          0 AUTO-CAPTURE   09-OCT-12 17.11.18 YES     NO       YES
SQL_PLAN_6d9bz8qqhnxu41479c4b0          59259          0 AUTO-CAPTURE   09-OCT-12 17.41.17 YES     NO       YES

Параметр, формально отвечающий за автоматическое формирование истории планов выполнения, установлен в значение по умолчанию, т.е. отключен:

SQL> @param optimizer_capture_sql_plan_baselines

NAME                                 VALUE  IS_DEF  IS_MOD  DSC
------------------------------------ ------ ------- ------- -----------------------------------------------------------------
optimizer_capture_sql_plan_baselines FALSE  TRUE    FALSE   automatic capture of SQL plan baselines for repeatable statements

6 комментариев »

  1. На счет Baseline, с курсов помню, что даже не смотря на их наличие, Оракл будет перепроверять и строить запрос заново. SQL patch действительно удобно! Буду иметь ввиду. Мне вот только не совсем понятна цель. Даже если и долгий hard parse, то после первого выполнения запроса его план ведь сохранится и далее будет использоваться. В чем смысл его фиксировать? Сохранить результаты на случай сброса кэша, перезагрузки?

    комментарий от Sergey Golikov — 29.10.2012 @ 11:19 | Ответить

    • Согласен, Сергей, факт известный, однако для полного hard parse при зафиксированном в baseline плане нужны какие-то основания, что-то в окружении, объектах бд или их содержимом должно меняться, но всё получается проще — каждый разбор создаёт новую версию курсора. Было бы интересно узнать как этим поведением управлять?

      комментарий от Igor Usoltsev — 29.10.2012 @ 15:07 | Ответить

      • Возможно, я не сталкивался с такой задачей, где в запросе нужно использовать 180 таблиц. Если я правильно понимаю, основное назначение средств управления планами запроса — стабилизировать запрос, чтобы его план не менялся в зависимости от изменения окружения, объема данных в таблице в зависимости от дневного/ночного профиля нагрузки и т.д. Но ведь парсинг — разовая задача, на написание запроса с 180 таблицами наверняка ушел ни один час, и ради такого случая можно и подождать первый раз 27 секунд. Чисто теоретически научиться управлять и разобраться в этом, конечно интересно. Но на практике так ли часто это требуется влиять на время разбора?

        комментарий от Sergey Golikov — 29.10.2012 @ 15:39 | Ответить

        • Нечасто,в основном в OEBS в виде жалоб пользователей на медленное выполнение редковыполняемых действий — классический случай для парсинга. Так что интерес в большой степени — спортивный :)

          комментарий от Igor Usoltsev — 30.10.2012 @ 12:16 | Ответить

  2. Игорь! А где взять sessof ? Спасибо.

    комментарий от Sergei Shumeev — 23.12.2014 @ 14:30 | Ответить

    • sesson.sql, ну и sessof.sql там же следом добавил)

      комментарий от Игорь Усольцев — 23.12.2014 @ 18:01 | Ответить


RSS feed for comments on this post. TrackBack URI

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

Блог на WordPress.com.

%d такие блоггеры, как: