Оптимизация динамического списка




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2025-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

20 Comments

  1. MoGar

    «Если на список накладывается несколько отборов, очень важен их порядок (на текущей версии платформы 1С). Правильный порядок отборов:»

    а проверяли план запроса в зависимости от порядка условий? Ведь при построении плана запроса сервер SQL сам может поменять порядок отборов, если посчитает нужным (в зависимости от тех же индексов и др. условий).

    Reply
  2. smirnov0ser

    (1) MoGar,

    Проверял, сейчас повторно запустил проверку с 2 отборами двумя вариантами:

    1) Отбор по пустому менеджеру; отбор по вычисляемому полю — 458мс

    2) Отбор по вычисляемому полю; отбор по пустому менеджеру — 2278мс

    В тексте выполненных sql запросов различия в порядке условий сохранились, планы запросов разные.

    К сожалению, во втором случае ms sql не поменял порядок условий, а также выбрал неоптимальный план запроса. Повлиять на это со стороны 1С пока не представляется возможным, кроме как накладывать отборы в «правильном» порядке.

    Проверено также на типовой БП3.0, в запрос полного списка проводок добавлено «тяжелое» вычисляемое поле:

    (ВЫРАЗИТЬ(ХозрасчетныйДвиженияССубконто.Регистратор.Номер КАК СТРОКА(10))) + «_ЛЯЛЯ» КАК ЛЯЛЯ

    На список накладывал 2 вида отборов:

    1) СчетДт = 60.01; ЛЯЛЯ содержит «ЛЯ»,

    2) ЛЯЛЯ содержит «ЛЯ»; СчетДт = 60.01,

    По сумме времени запросов ( по данным profiler’а), первый вариант отработал в 3,6 раза быстрее, чем второй.

    Если есть возможность, проверьте у себя, было бы интересно увидеть результаты на другой машине.

    Reply
  3. herfis

    По поводу флага «Динамическое считывание данных». Очень для меня интересный момент.

    Исходя из ваших рекомендаций, я понял что почти всегда имеет смысл его отключать, кроме случая тяжелых запросов и малой вероятности необходимости скролла. Т.е. можно все бросать и бежать снимать галку во всех формах простых журналов документов? Ведь скролл будет комфортнее работать и реже подтормаживать. Почему тогда по дефолту галка всегда стоит? Может, не все так гладко? Я понимаю, что минусом будет увеличение вероятности отображения неактуальной информации. Других минусов нет?

    Reply
  4. herfis

    Поставил эксперимент, получил странный результат.

    При снятии галки динамического считывания, подтормаживания при скроллах остались в рамках тех же порций, что и были. Т.е. чуть больше размера экрана.

    Но при этом на динамическом списке с произвольным запросом с соединением среза последних по регистру сведений подтормаживание резко увеличилось. Т.е. похоже на то, что из БД выбирается в самом деле увеличенная порция данных, но выбирается она с той же самой периодичностью! Т.е. никакого профита — одни убытки. Возможно, дело в используемой СУБД.

    Основные таблицы везде прописаны.

    сервер 8.3.6.2390 на x64 Ubuntu 14.04, сервер PostgreSQL 9.2.4 на другом x64 Ubuntu 14.04

    В общем, осторожней с этой галкой.

    Reply
  5. lkishko
    Поля, по которым чаще всего устанавливаются отборы должны быть проиндексированы

    имхо, нужно тоже соблюдать баланс индексов. когда их много, то таблицам это не очень нравится… как обычно, используем здравый смысл ))

    Reply
  6. smirnov0ser
    Reply
  7. herfis

    (6) Еще раз.

    При снятии галки на «тяжелом» динамическом списке наблюдалось резко увеличившееся подвисание при прокрутке. Не при длительной прокрутке, а при обычной. Если бы дело было только в обращении на сервер приложений за порцией уже считанных данных, такого бы не наблюдалось. Время «подвисания» должно было либо остаться таким же, либо уменьшиться. Так что либо при обычной прокрутке каждый раз фигачится запрос на 1000 элементов, либо пропала какая-то другая оптимизация.

    Reply
  8. smirnov0ser

    (7) herfis,

    Странно, что подвисания увеличиваются, у меня обратная ситуация.

    http://its.1c.ru/db/v8std/content/2149184381/hdoc/_top/%E4%E8%ED%E0%EC%E8%­F7%E5%F1%EA%EE%E5%20%F1%F7%E8%F2%FB%E2%E0%ED%E8%E5%20%E4%E0%­ED%ED%FB%F5

    Здесь подтверждается, что при запросе 1000 строк, данные запоминаются в буфере на сервере, а потом, передаются на клиент частями. Видимо у Вас этот буфер работает медленнее, чем повторный запрос к БД.

    На своей базе проверил — при отключенном динамическом считывании, при небольшой прокрутке запрос к БД не происходит. И прокрутка быстрее, чем при динамическом считывании.

    Reply
  9. herfis

    (8) Я верю, что у вас так. Это, по крайней мере, логично.

    Но в моей конфигурации, которую я описал выше, все не так радужно.

    Вполне может быть что это косяк в линуксовой версии сервера приложений.

    Reply
  10. pyrkin_vanya

    А я вот не совсем понял, что значит эта фраза «При соединении таблиц использовать индексы»? Использовать временные таблицы в запросе динамического списка нельзя. Про какие индексы вы говорите? Поясните пожалуйста.

    Reply
  11. herfis

    (10) Речь о так называемом «попадании в индекс». Т.е. стараться в запросах эффективно использовать существующие индексы физических таблиц, а при острой необходимости — создавать недостающие. Самое банальное для примера — если в запросе динамического списка используется соединение с очень большим справочником по доп-полю с высокой селективностью, то имеет смысл это доп-поле проиндексировать.

    Reply
  12. pyrkin_vanya

    (11) Как? Можно пример?

    Reply
  13. smirnov0ser

    (12) Индексы физических таблиц. Например, в динамическим списке связывается физическая таблица справочника «Товары» и РС «Состояние контрагентов» по полю «Контрагент». Так вот желательно, чтобы и в справочнике — реквизит «Контрагент» был проиндексирован (это свойство реквизита), и в регистре — измерение «Контрагент» было проиндексировано (свойство измерения, имеет смысл если оно не первое измерение).

    Reply
  14. pyrkin_vanya

    А, менять индексирование в реквизите. Это не подходит. Без изменения конфы надо.

    Reply
  15. smirnov0ser

    (14) У вас какой-то типовой динамический список подтормаживает?

    Без изменения конфигурации проиндексировать не получится (средствами sql можно, но нежелательно).

    Reply
  16. pyrkin_vanya

    Проблема немного другая. Есть УТ 11.3. В ней есть форма списка справочника номенклатура. Вот запрос ,что там используется.

    ВЫБРАТЬ
    СпрНоменклатура.Ссылка КАК Ссылка,
    СпрНоменклатура.Код КАК Код,
    СпрНоменклатура.Наименование КАК Наименование,
    СпрНоменклатура.Артикул КАК Артикул,
    СпрНоменклатура.ВидНоменклатуры КАК ВидНоменклатуры,
    СпрНоменклатура.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    СпрНоменклатура.СтавкаНДС КАК СтавкаНДС,
    СпрНоменклатура.Родитель,
    ВЫБОР
    КОГДА СпрНоменклатура.ЕстьТоварыДругогоКачества
    ТОГДА 4 + ВЫБОР
    КОГДА СпрНоменклатура.ПометкаУдаления
    ТОГДА 1
    ИНАЧЕ 0
    КОНЕЦ + ВЫБОР
    КОГДА СпрНоменклатура.ИспользованиеХарактеристик = ЗНАЧЕНИЕ(Перечисление.ВариантыИспользованияХарактеристикНоменклатуры.НеИспользовать)
    ТОГДА 0
    ИНАЧЕ 2
    КОНЕЦ
    ИНАЧЕ ВЫБОР
    КОГДА СпрНоменклатура.ПометкаУдаления
    ТОГДА 1
    ИНАЧЕ 0
    КОНЕЦ + ВЫБОР
    КОГДА СпрНоменклатура.ИспользованиеХарактеристик = ЗНАЧЕНИЕ(Перечисление.ВариантыИспользованияХарактеристикНоменклатуры.НеИспользовать)
    ТОГДА 0
    ИНАЧЕ 2
    КОНЕЦ
    КОНЕЦ КАК ИндексКартинки,
    ВЫБОР
    КОГДА СпрНоменклатура.aspect_АдресКартинки = «»
    ТОГДА ЛОЖЬ
    ИНАЧЕ ИСТИНА
    КОНЕЦ КАК Картинка,
    СпрНоменклатура.aspect_СредняяПродажа КАК СредняяПродажаВМесяц,
    ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ВНаличииОстаток,
    ТоварыНаСкладахОстатки_СкладМастерок.ВНаличииОстаток КАК ВНаличииОстаток_СкладМастерок,
    ТоварыНаСкладахОстатки_СкладМагазин.ВНаличииОстаток КАК ВНаличииОстаток_СкладМагазин,
    СпрНоменклатура.aspect_ЗакупочнаяЦена КАК ЗакупочнаяЦена,
    СпрНоменклатура.aspect_РозничнаяЦена КАК РозничнаяЦена,
    СпрНоменклатура.aspect_ДатаПоследнегоИзменения КАК ДатаПоследнегоИзменения,
    СпрНоменклатура.aspect_ДатаПоследнейПродажи КАК ДатаПоследнейПродажи
    ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, ) КАК ТоварыНаСкладахОстатки
    ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &СкладМастерок) КАК ТоварыНаСкладахОстатки_СкладМастерок
    ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки_СкладМастерок.Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &СкладМагазин) КАК ТоварыНаСкладахОстатки_СкладМагазин
    ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки_СкладМагазин.Номенклатура
    ГДЕ
    НЕ СпрНоменклатура.ЭтоГруппа
    {ГДЕ
    (СпрНоменклатура.Ссылка В
    (ВЫБРАТЬ
    Сегменты.Номенклатура
    ИЗ
    РегистрСведений.НоменклатураСегмента КАК Сегменты
    ГДЕ
    Сегменты.Сегмент = &СегментНоменклатуры))}

    Показать

    ПРоблема в том, что иногда открываешь форму и она открывается через 2-3 секунды, а иногда через 10 минут. Вот и не понимаю почему. Может подсжадите?

    Reply
  17. smirnov0ser

    В первую очередь, проверьте «вытащенные через точку» поля в пользовательском режиме. Их отключение может увеличить производительность.

    База на SQL? Попробуйте пересчитать статистику и перестроить индексы, часто именно это является причиной нестабильной работы.

    Если ничего не помогло — нужен детальный анализ через профайлер.

    Ну и как вариант, чтобы убедиться, что причиной долгого открытия формы является именно динамический список, попробуйте отключить его.

    Reply
  18. pyrkin_vanya

    Поля через точку примитивного типа. База файловая. Без динамического списка все летает.

    Reply
  19. baracuda
    Поля, по которым чаще всего устанавливаются отборы должны быть проиндексированы.

    а как понять какие поля проиндексированы? В регистре накопления например, есть ресурсы есть измерения, что из них должно индексироваться?

    Reply
  20. frkbvfnjh

    Статья помогла найти в чем причина зависания при открытии формы списка регистра сведений с динамическим списком. Спасибо!

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *