Быстрая индексированная выборка элементов справочника по коду




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

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

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

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

18 Comments

  1. AlX0id

    мм.. А если добавить реквизит, аналогичный коду с указанием индексации — не прокатит?

    Сам не пробовал на таких объемах — просто спрашиваю )

    Reply
  2. Armando

    Меня вот это смущает:

    Соединение таблицы справочника и временной таблицы с кодами не привело к использованию индекса
    Reply
  3. vvr908

    А какая была СУБД?

    Reply
  4. Famza

    (0)

    Соединение таблицы справочника и временной таблицы с кодами не привело к использованию индекса.

    А какой был первоначальный запрос? Ладно бы для 7.7 проблема субд и запросов, а для 8.2…

    Reply
  5. Evilgrym

    (4) Famza,

    Первоначальный вариант был такой:

    //——————————————

    Выбрать

    тз.код

    Поместить СписокКодов

    из &tz как ТЗ

    Индексировать по Код

    ;

    Выбрать

    Спр.код,

    Спр.ссылка

    из

    Справочник.какойтотам как Спр

    ВНУТРЕННЕЕ СОЕДИНЕНИЕ СписокКодов как СписокКодов

    по Спр.код=СписокКодов.код

    //———————————————

    Reply
  6. Evilgrym

    (3) vvr908,

    Postgre SQL 9.1

    Reply
  7. Evilgrym

    (1) AlX0id,

    Не прокактит. 1С-ка создает индекс, типа

    CREATE UNIQUE INDEX _reference2_byfield2510_rr

    ON _reference2

    USING btree

    (_fld2508rref, _idrref);

    Т.е. в этом индексе 2 колонки — интересующее нас поле и ссылка на элемент справочника.

    Но в запросе мы используем условие всего лишь по одному полю из двух. В результате Postgre SQL считает этот индекс неподходящим и лезет перебором по всей таблице.

    Да, и так, для статистики — в справочнике без малого 14 млн записей. Записи постоянно добавляются и используется по большей части последняя, самая свежая, треть. Поэтому самодельный индекс сделан убывающим, чтоб свежие данные были в более быстром доступе. 1С-ка так не умеет.

    Reply
  8. tango

    новосозданный индекс потом какой жизнью живет?

    1с его не знает.

    БД обновляет?

    если нет — то это как бы разовая фича, время на ее создание надо учитывать

    если да — то шаманство в пофигураторе может дать такой же эффект?

    Reply
  9. Evilgrym

    (8) tango,

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

    Нюанс может возникнуть при создании новой базы и при изменении в структуре данных. Индекс может пропасть, но это дело поправимое.

    Создавать индекс можно и не выгоняя всех из базы, используя команду CREATE INDEX CONCURRENTLY. Думает подольше конечно.

    Reply
  10. Evilgrym

    И вообще, по наблюдениям, все индексы, которые создает 1С-ка содержат в себе как минимум 2 поля, индексируемое и _idrref, а это приводит к тому что в реальной жизни, полной извращенных ситуаций, субд отказывается их использовать.

    Например в справочнике на 82 колонки имеется 65 индексов созданных конфигуратором, но выборки в основном делаются с помощью построителя запросов с отбором по нескольким колонкам и из 65 индексов хоть как-то используется только 18. Остальные вообще бесполезны. Ну ладно там в таблице всего 80 тысяч записей… Место-то жрется…

    Reply
  11. tango

    (10)

    вернее дает команду субд их создать

    мы ведь тоже тольк дали команду…

    почему индекс через пофигуратор содержит второе поле?

    может быть, это не совсем те индексы, которые БД создает по нашему криту?

    Reply
  12. tango

    стремно как-то в типовых убивать индексы от разрабов

    Reply
  13. tango

    вообще, любопытная картинка:

    ЗУП, СотрудникиОрганизаций

    ДатаДоговора — индекс

    ДатаПриемаНаРаботу — не индекс

    приколисты там, у флагмана, сидят

    Reply
  14. Evilgrym

    (12) tango,

    Ну я и не агитирую за их убийство.

    Просто, когда база под 150 гиг, как-то мысли всякие начинают в голову приходить, где б такое сэкономить да подускорить…

    Reply
  15. tango

    (14) я тут бегло глянул на ЗУП — «убивать», конечно, не комильфо. скажем так: «убрать»

    Reply
  16. aspirator23

    (10)Evilgrym, 82 колонки имеется 65 индексов — имеет смысл?

    С одной стороны запрос столько индексов не может использовать. С другой стороны получаем задержки при записях из-за расходов на записи индексов.

    Reply
  17. rus128

    «Задача усложнялась теще»

    а тёща тут при чем? 🙂

    Reply
  18. madkat

    У Вячеслава Гилева есть хороший инструмент, по анализу индексов. Вот я ему предлагал написать подсистему к любой стандарной конфе, в которой можно было указать для какого объекта и по каким полям проверять наличие инексов, и в случает отсутствия их создавать.

    Reply

Leave a Comment

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