Переписываем запросы 1С для повышения производительности на SQL сервере




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

24 Comments

  1. Йожкин Кот
  2. AlX0id

    (1) Йожкин Кот,

    Случай того, что «неоптимизированный» запрос выигрывает по всем статьям у «оптимизированного»? )

    Reply
  3. Gilev.Vyacheslav

    в этой статье много «теоретических рассуждений» и очередное изобретение велосипеда

    все это может не сработать, если в запросе например будет два десятка таблиц, а такой простой запрос в реальной жизни вряд ли будет создавать серьезные проблемы

    но такие статьи нужны, пока обучаешь других хотя бы сам учишься )

    Reply
  4. jan27

    (1) не могу посмотреть, что там?

    Reply
  5. jan27

    (3) спасибо за внимание. Мне показалось, что рассуждений здесь маловато. Не ставилась задача показать как решать серьезные проблемы. Кстати, а не найдется ли у вас примера запроса, вызывающего Intra-query parallelism deadlocks?

    Reply
  6. PVG_73

    Эта статья говорит лишь о том, что оптимизировать можно и почему (поверхностно….)

    А на самом деле такие статьи нужно рассматривать на конкретном примере:

    Есть объект метаданных у него есть такие реквизиты, по таким то реквизитам строятся индексы.

    Далее смотрим как меняется план выполнения SQL при той или иной выборке по данной таблице.

    И тогда сразу будет видно отчего именно зависит какой план выполнения выберет SQL.

    А в идеале рассмотреть выборку по нескольким таблица и показать как при правильном использовании тех или иных конструкций мы получаем оптимальный план выполнения….

    И на самом деле очень хорошо это видно на самом запросе SQL, т.к. транслятор 1С все равно по своему интерпретирует наши запросы к базе.

    Reply
  7. jan27

    (6) синтаксис указанных запросов в СКЛ один в один как в 1С разве что англоязычный и поля замененены на имена СКЛ

    да. в ходе экспериментов попадались варианты, когда один запрос 1С вызывает множество коротких запросов СКЛ, причем часть из них — работа с временными таблицами,

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

    конкретные примеры сильно привязаны к специфике конкретного запроса, хотелось показать общий подход

    Reply
  8. PVG_73

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

    Reply
  9. jan27

    (8) это да, не спорю… но индексы можно и добавить по итогам анализа планов запроса, избыточные индексы тоже не есть гуд

    Reply
  10. BabySG

    (4) не следует использовать условие по ИЛИ, сделайте через ОБЪЕДИНИТЬ ВСЕ. Кстати, на 1С:Эксперт по это тоже рассказывают.

    Reply
  11. jan27

    (10) да, согласен, по ИЛИ отрабатывает медленнее, что и показано в работе

    Reply
  12. AlX0id

    Я бы хотел еще уточнить одну вещь — профайлер показывает помимо длительности запроса еще логические чтения и число строк. Каким образом, например, сравнивать запросы с чуть большей длительностью и меньшим количеством чтений с запросами меньшей длительности, но на порядок большим количеством чтений?

    Reply
  13. jan27

    (12) если речь о рабочем сервере, профайлером не рекомендуют пользоваться, а так в профайлере можно группировать по различным колонкам, выгружать данные и сортировать по интересующим колонкам

    Reply
  14. AlX0id

    (13)

    Вот меня и интересует, какие колонки должны интересовать в каких случаях %)

    Reply
  15. jan27

    (14) в большинстве случаев увеличения колонки Reads влекут за собой и увеличение в колонке Duration

    Reply
  16. zoytsa

    (3) Gilev.Vyacheslav,

    интересна вставка временной таблицы, Ваша цитата с gilev.ru


    Во многих случая ускорить запрос могут временные таблицы

    Сообщение Гилёв Вячеслав » 15 окт 2013, 01:23

    … могут, но не всегда.

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

    Минусы:

    для создания временной таблицы требуется время;

    для каждого соединения свои изолированные таблицы;

    временные таблицы при заполнении большим объемом данных тратят много времени;

    создание индексов для временных таблиц это еще дополнительное время;

    осторожнее с ораклом, включая 11 версию субд плохо дело со статистикой;

    слишком большое количество временных таблиц может занять много места на диске.

    Плюсы:

    в отличии от вложенных запрос количество строк во временной таблице ПРОГНОЗИРУЕМОЕ, именно «ясность» с объемом выборки делает их удобными для оптимизации мест, которые выполняются неоправдано долго;

    временные таблицы не пересекаются по блокировкам;

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

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

    виртуальные таблицы — это платформенное использование временных таблиц, но иногда бывают ситуации, когда временная таблица из-за большей «управляемости» может быть более интересная, чем виртуальная таблица…

    Показать

    и еще:

    Если в запросе используется соединение с виртуальной таблицей языка запросов «1С:Предприятия» (например, «РегистрНакопления.Товары.Остатки()») и запрос работает с неудовлетворительной производительностью, то рекомендуется вынести обращение к виртуальной таблице в отдельный запрос с сохранением результатов во временной таблице.

    Виртуальные таблицы, используемые в языке запросов «1С:Предприятия», могут разворачиваться в подзапросы при трансляции в язык SQL. Это связано с тем, что виртуальная таблица часто (но не всегда) получает данные из нескольких физических таблиц СУБД. Если вы используете соединение с виртуальной таблицей, то на уровне SQL оно может быть в некоторых случаях реализовано как соединение с подзапросом. В этом случае оптимизатор СУБД может точно так же выбрать неоптимальный план, как при работе с подзапросом, использованным в языке «1С:Предприятия» в явном виде

    Reply
  17. Gilev.Vyacheslav

    (16) zoytsa, Не понял сути сообщения, да и лучше писать в наш форум http://www.gilev.ru/forum/, если хотите услышать ответ от нас гарантированно

    Reply
  18. jan27

    (17) некрасиво использовать чужую статью для собственной рекламы, уж если начали обсуждать — продолжайте

    Reply
  19. lustin

    (0) А все таки у меня вопрос чем вызвана такая любовь к чистой ИТСовской обработке Консоль запросов. Есть же http://infostart.ru/public/56973/

    Reply
  20. jan27

    (19) если присмотреться, то можно увидеть, что она не чисто ИТС — http://www.lavelin.ru. Лично мне она нравится тем, что можно применять произвольный код к результату запрса

    Reply
  21. jobkostya1c8

    Полезный материал для специалистов по оптимизации и производительности конфигураций 1С. Вот только Profiler нужно в меру использовать. Иначе как пошутили эти самые оптимизаторы «если запрос все равно в 1С криво выполняется и не понять в чем причина придется в Profiler лезть смотреть. Вот только если ты будешь лазить в профайлер с каждым кривым запросом то будешь два рубля в день получать. Не в смысле две тысячи :)»

    Reply
  22. jan27

    (21) а у вас все запросы такие кривые?))

    Reply
  23. amaksimov

    (22) Константин дело говорит.

    Мне сегодня пригодилась, например, хранимая процедура sp_WhoIsAcitve, она также позволяет планы запросов получать (причём также в SQL графически его сразу видно). Исполняется так в SQL Server Management Studio:

    exec sp_whoisactive

    @get_full_inner_text = 0

    ,@get_plans = 1

    ,@get_outer_command = 1

    Вложил хранимку в zip вложении.

    Reply
  24. jan27

    (23) спасибо

    Reply

Leave a Comment

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