<?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='\
абалдеть !
Как всегда прочитал с удовольствием и обязательно буду применять в работе.
Очень красиво, спасибо большое. И вдобавок ещё и полезно. 🙂
Очередная статья мастера. По заголовку статьи знаешь, чья статься. Спасибо.
Очень интересно и наглядно.
Вопрос, Не пытались архитектуру регистра заточить под использование кластерного индекса? Судя по последнему плану запроса основную часть ресурсов потребляет операция «Поиск ключа», судя по всему это проверка части условия «..ДатаСменыСтатуса > &Т0..» + получение ресурса «Статус», сделав ДатаСменыСтатуса в составе измерений, уже можно получить профит, а если кластерным будет ключ [Проекция, Период, ДатаСменыСтатуса…], то должны получить максимальную скорость.
(4) Sergey.Noskov, такая мысль была.
Не стал этого делать, поскольку попробовал этот вариант на основной задаче: поиске интервалов. Первый план ему как раз и соответствует. Ощутимого прироста это не дало, поэтому на задаче о статусах пробовать уже не стал.
Не хочу придираться к сути самой статьи, там все, наверное, правильно,
но для чистоты эксперимента в «Задаче о статусах заявок» нужно все-таки сравнивать быстродействие вот с этим запросом:
ВЫБРАТЬ * ИЗ РегистрСведений.СтатусыЗаявок.СрезПоследних(&Т0, Статус В (&Новый, &ВРаботе))
При условии, конечно, что Статус — индексированный
В этом случае результаты будут ИМХО не такие радужные
(6) vspl, это не эквивалентные запросы.
Спасибо, красиво!
(6) При небольшом количестве статусов (я думаю, до 50, могу проверить) СрезПоследних без явного отбора по заявкам всегда будет уходить в скан. Про это и была исходная задача. Задача описанная в этой статье — это вторая часть той задачи и она не эквивалентна срезу да и не решается только через срез.
ildarovich на самом деле молодец, сразу увидел главную граблю задачи по интервалам.
Задача со статусами , ИМХО, проще решается путем ввода условия
РегистрСведений.СтатусыЗаявок.СрезПоследних(&Т0, Период>=&ДатаПотериИнтереса)
, где ДатаПотериИнтереса =Т0- ИнтервалИнтересаВСекундах
, где ИнтервалИнтересаВСекундах — интервал, дальше которого пофиг на все незакрытые статусы. Такой интервал в реальных задачах всегда есть. Пусть даже большой. Например, год или два.
(10) bulpi, Если этот интервал больше 10% от общего времени жизни — будут сканы. Точнее так: в 8.2 будет сканирование большого диапазона кластеризованного индекса, в 8.3 скорее всего будет полный скан (судя по освещённым в Sergey.Noskov изменениям).
(5) а в диаграмме значения «Без индексирования» и «С индексированием» это сравнение каких запросов? Можно план запроса для «Без индексирования» увидеть?
(12) Sergey.Noskov, «с индексированием» это запрос с отбором по проекции:
Без индексирования — исходный запрос без такого отбора:
Вот его план (в двух видах в приложенных файлах):
Не увидел там ничего интересного, поэтому не стал приводить в статье.
Правда есть один забавный момент — есть такая константа «НеИспользоватьРазделениеПоОбластямДанных», так вот чтобы был доступен пункт с дополнительными отчетами и обработками она должна быть установлена в «Истина». Устанавливаться она должна, по идее, автоматически, но, видимо, разработчики БСП где-то что-то упустили и по умолчанию установлено значение «Ложь».
Можно зайти через «все функции», найти эту константу и установив значение «Истина» получить доступ к настройке внешних отчетов и обработок.
крутатень! Ещё одно доказательство, что Правильная архитектура — очень важна!
мат. подход нужен, когда уже не помогает подход с матом
Решил «реиндексировать» РС «СостоянияСогласованияЗаявок» УПП. При заполнении реквизита «Проекция» периодически возникала ошибка «Неправильное значение аргумента встроенной функции (Log)». Анализ показал, что в случае когда соседние записи в РС идут с одной и той же датой в поле период, то код
выдает ошибку, т.к. log(0) не существует. Как решить эту проблему? Я понимаю, что записи в пределах секунды разнесены, но как учесть это в алгоритме расчета проекции не понимаю.
(17)Почему бы для До, От не использовать МоментВремени()?
(18) между моментами времени нельзя вычислить разность.
(17) Проще всего заменить выражение
выражением
Тогда в следующем цикле будет выполняться больше итераций, а результат будет тем же. Ну, а если все же хочется убрать «лишние» итерации, то нужно проверить условие «От = До». Если оно выполняется, то масштаб принять равным единице:
Кажется, так.
(20) Спасибо. Сейчас попробую.
Еще такой момент. Возможно обработка «Реиндексация» писалась на скорую руку и предполагалось, что конечный пользователь сам ее оптимизирует. Спасибо и за это. А возможно что-то упускаю и нужно было сделать именно так, как сделано.
Вот что есть сейчас:
При наличии в ТЗ ~2,5 млн строк Таблица.Сортировать(«Заявка, Период») выполняется оч долго. Сделал так:
Показать
(21) Если вы про обработку «Реиндексация» из примеров к статье, то все так и было.
Быстрое определение интервалов в запросе . Если постараться, то и проекцию можно сразу в запросе посчитать.
Если реиндексацию необходимо делать не один раз, а часто, то интервалы лучше находить на основе алгоритма из статьи: