Проверка таблицы на дублирование строк (ТаблицаЗначений, ТабличнаяЧасть) и получение массива таких строк




Принцип обмена данными из 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='\

27 Comments

  1. Hamsik

    Добрый день, не очень понятно зачем так много когда для такой задачи, да и практическое применение тоже не очень понятно, пробовали на большом количестве строк?

    Reply
  2. uri1978

    (1) Если можно реализовать этот функционал меньшим количеством строк кода буду рад увидеть. На больших не пробовал.

    Reply
  3. pm74

    (2) через запрос будет короче

    Reply
  4. uri1978

    (3) «буду рад увидеть»

    Reply
  5. uri1978

    Проверять можно не только ТабличнуюЧасть, но и любую ТаблицуЗначений.

    Reply
  6. kuzev

    (3) регистрозависимо?

    Reply
  7. uri1978

    (6) Что именно? Если передача имен колонок — то нет, неважно в каком регистре. Если содержание таблицы — да, регистрозависимо.

    Reply
  8. pm74

    (6)

    регистрозависимо

    что имеется в виду ?

    Reply
  9. kuzev

    (8) «а» и «А»

    Reply
  10. pm74

    (9) ах вот что , ну да для текстовых строк

    Reply
  11. acsent

    А нельзя ли было однопроходным алгоритмом по сортированной таблице сделать?

    Reply
  12. uri1978

    (11) Хотелось бы увидеть такой код

    Reply
  13. МихаилМ

    (12)

    такой код есть в Искусство программирования. Том 1—4»

    Автор: Дональд Эрвин Кнут

    Reply
  14. Идальго

    Вот для основы:

    Функция ПроверитьНаличиеДублейГрупп()
    
    Запрос = Новый Запрос(
    «ВЫБРАТЬ
    | ТаблицаГруппы.Группа КАК Группа
    |ПОМЕСТИТЬ
    | ГруппыТемы
    |ИЗ
    | &ТаблицаГруппы КАК ТаблицаГруппы;
    |
    |ВЫБРАТЬ
    | ГруппыТемы.Группа КАК Группа
    |ИЗ
    | ГруппыТемы КАК ГруппыТемы
    |СГРУППИРОВАТЬ ПО
    | ГруппыТемы.Группа
    |ИМЕЮЩИЕ
    | КОЛИЧЕСТВО (*) > 1»);
    
    Запрос.УстановитьПараметр(«ТаблицаГруппы», ГруппыНоменклатуры.Выгрузить(,»Группа»));
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    #Если Клиент Тогда
    Пока Выборка.Следующий() Цикл
    ТекстОшибки = «Группа «+Строка(Выборка.Группа)+» повторяется.»;
    Сообщить(ТекстОшибки);
    КонецЦикла;
    #КонецЕсли
    
    Возврат РезультатЗапроса.Пустой();
    
    КонецФункции
    

    Показать

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

    Reply
  15. uri1978

    (14) Поиск задублированных строк осуществляется следующим кодом, всё остальное в функции довесы. Причем код универсальный для любой таблицы значений и любого количества колонок:

    ВремТаблицаЗначений = Таблица.Выгрузить(,Колонки);
    ВремТаблицаЗначений.Колонки.Добавить(«Количество_fhgeE»);
    ВремТаблицаЗначений.ЗаполнитьЗначения(1, «Количество_fhgeE»);
    ВремТаблицаЗначений.Свернуть(Колонки, «Количество_fhgeE»);
    ВремТаблицаЗначений.Сортировать(«Количество_fhgeE» + » Убыв»);  

    То что происходит выгрузка ТЗ, так ведь и в запрос её нужно передать. То что есть сворачивания и сортировки естественно есть и в запросе.

    Вариант запросом рассматривался, выбран был этот вариант.

    Reply
  16. acsent

    (12) Типа идешь вниз по таблице, если ключевые занчения не поменялись, то это дубль

    Reply
  17. uri1978

    (16) Вариант.

    Но боюсь, что интерпретатор 1С при проходе всех строк будет работать намного дольше чем «Свернуть», «Отсортировать», тем более придется сравнивать все ключевые поля на каждой итерации.

    Reply
  18. acsent

    (17) нужно устроить конкурс по скорости поиска дублей )))

    Reply
  19. independ

    Как то делал так, насчет скорости не замерял

    Для каждого стр из ТЗ Цикл
    ПараметрыОтбора=Новый Структура;
    Для каждого Колонка из ТЗ.Колонки Цикл
    ПараметрыОтбора.Вставить(Колонка.Имя,стр[Колонка.Имя]);
    КонецЦикла;
    стрНайд=ТЗ.НайтиСтроки(ПараметрыОтбора);
    Если стрНайд.Количество()>1 Тогда
    
    КонецЕсли;
    КонецЦикла;
    

    Показать

    Reply
  20. uri1978

    (19) Будут найдены дубли, а потом по ним снова поиск дублей.

    Reply
  21. independ

    (20) да, но можно сделать некую проверку, или выгрузить, а затем свернуть. Но на большой ТЗ, думаю будет не очень

    Reply
  22. uri1978

    (21) 🙂 🙂

    Reply
  23. IssakN

    Благодарю автора за приведенный код — пригодился в работе. Один вопрос — Колонки если нужно сделать отбор по двум — нужно вызывать функцию соответственно дважды, передавая разное значение переменной Колонки?

    Reply
  24. uri1978

    (23)

    Пример таблицы:

    Колонка1 | Колонка2

    10 | 1

    10 | 2

    1. Если Вы хотите проверить задублированные строки по паре значений — нужно передавать через запятую названия 2 колонок.

    «Колонка1,Колонка2» — этом случае задвоенных значений нет.

    2. Если же Вы хотите проверить для каждой из колонок наличие дублей — то вызывать соответственно два раза.

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

    Reply
  25. IssakN

    (24) Благодарю пригодился второй способ.

    Reply
  26. brylig

    (19) Вот тут так для табличных частей сделал без повторных проверок https://forum.infostart.ru/forum9/topic44859/message2066170/

    Reply
  27. uri1978

    (26) Этот код видел, в теме отписался. Собственно эта тема и сподвигла выложить свою процедуру (давно валялась) 🙂

    Reply

Leave a Comment

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