<?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='\
Почему в виртуальной таблице нет условий на контрагента и организацию? Это, если верно помню, убыстряет дело, они ведь — первые два измерения. Смыслово это кажется избыточностью, но — Гилёв рекомендует… И второе — кто вам вот так легко даст переделывать регистры? Надо не только архитектуру курочить, но и к имеющейся верные запросы писать.
Мне кажется что достаточно было бы прописать в вирт. таблицу остатков отборы по Организации и Контрагенту (темболее что они однозначно определяются договором) это бы позволило использовать индекс табл. остатков без изменения порядка следования измерений.
И насколько я понимаю суть индексов, то даже измерение порядка следования без указания условий отбора по организации и Контрагенту не позволит на все 100 использовать индекс т.к. еще доп будет сканирование таблицы или индекса для отбора по этим измерениям.
Действительно, в данном случае нужно было использовать отбор в виртуальной таблице на организацию и контрагента, чтобы не «портить» регистр и использовать уже имеющийся индекс. Так как если регистр «тяжело» используется в конфигурации, нужно перелопачивать конфигурацию в поисках запросов к этому регистру с целью их исправления. Приходилось этим заниматься один раз, но это было оправдано — прирост производительности составил до шести раз. А вот индексация договора — правильная идея: именно для отбора в таблице движений, так как договор мельче «гранулирует» таблицу движений, тем самым меньше записей будет блокироваться. Автору статьи рекомендую почитать статьи с диска ИТС (раздел про вопросы крупных внедрений, кажется), там все эти вещи про блокировки есть. А также книжку Габец «Реализация прикладных задач в системе 1С Предприятие 8.2» из серии «Профессиональная разработка». Хотя если удобнее через профайлер изучать — тоже способ :).
(1) Yashazz, Если бы мне «не дали» изменить структуру регистра — я бы так и поступил — добавил бы условие по организации и контрагенту в отбор виртуальной таблицы.
(2) headMade, Я смотрел план выполнения запроса в профайлере и по таблице итогов Clustered index seek выполняется без дополнительных условий (секции «Predicates» нет) и все поля отбора попадают в секцию «Seek predicates» — из этого я делаю вывод, что доп. сканирования таблицы не происходит…
(5) Александр, я думаю, что вы несколько погорячились с перемещением измерений типа Организация (а возможно и Контрагент) на самые нижние позиции. С точки зрения решения вашей локальной задачи, быть может вы и правы. Эффективность использования индекса действительно выше у самых первых. А вот если посмотреть на данную проблему в долгосрочной перспективе — то, скорее всего это ошибочное решение.
Дело в том, что измерения «Организация» и «Контрагент» — это базовые группировки для любых отчетов. Теперь же, все остальные отчеты, которые будут использовать любые таблицы данного регистра, резко понизят свое быстродействие. Особенно, это будет сказываться при декартовом соединении.
В качестве резюме можно сказать: успешное решение локальной (тактической) задачи, не всега удачно, для решения долгосрочной (стратегической) задачи. А иногда, может вызывать прямо противоположный эффект.
(0) Для полноты статьи я бы порекомендовал автору добавить информацию о том, почему вообще в его примере возникли блокировки, т.к. не для всех очевидно, что чтение в транзакции накладывает блокировки.
(0) Не совсем понятно, для чего вообще используется синтаксис
Если не используется соединение, то откуда там может взяться этот самый NULL?
А так, в целом, за пытливость ума и ковыряние в носу — наверное плюс
(8) director04,
ну откуда берутся NULL в базе 1с? там все возможно словить..
Автор же просто привел тестовый пример ))
(0) Очень частный и не убедительный пример. Регистр РасчетыПоПриобретениюВВалютеОрганизации, имхо, не такой критичный по производительности в базе данных.
Есть гораздо более «тяжелые» регистры. Почему автор не приводит пример их «успешной» переделки?
(8) director04,
Мне вообще неочевиден смысл конструкции ISNULL(СУММА(Поле1),0).
Думается, что на NULL надо проверять до суммирования: СУММА(ISNULL(Поле1, 0)).
(11) vvr908, NULL в запросах может появляться в одном случае — при соединении двух таблиц. Другие случаи мне неизвестны.
В данном случае, соединение не используется, то есть, проверка на NULL — тоже замедляет (в некоторой степени) выполнение запроса.
(12) director04, мы говорим о разных вещах.
Понятно, что в данном конкретном случае проверка вообще лишняя. Я просто заметил, что имеет смысл проверять значение на NULL перед тем, как мы попытаемся что-то с ним сделать (например, взять сумму), а не после.
(13) vvr908, Согласен, сумму с NULL брать неразумно ))))
(6) director04, Речь идет о таблице итогов, которая используется только в виртуальных таблицах, поэтому непосредственно с ней в запросах 1С соединение сделать невозможно. При соединении с виртуальной таблицей индексы вообще использоваться не будут, если только ее не проиндексировать с помощью временной таблицы…
(12) director04, Если мне не изменяет память, то есть еще один случай, когда в запросе даже БЕЗ соединений может появиться NULL.
Если для реквизита справочника свойство «Использовать» = «Для элемента» («Для группы»), то в запросе из справочника для групп (элементов) значение этого реквизита = NULL!