<?php // Полная загрузка сервисных книжек, создан 2025-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) Смотришь на ваши статьи по запросам и думаешь: «…Так, чем сегодня удивит ildarovich».
Мне нравятся Ваши статьи и у Вас подход совершенно не 1С-овский к решению задач. Обычно 1С-ники сначала сделают, а потом думают как оптимизировать.
Вы же идете совершенно по другому пути: теория, вперемешку с тестами и результатом всего этого является красивое и лаконичное решение да еще и запросом.
Язык запросов 1С сильно ограничен, но глядя на Ваши статьи начинаешь задумываться, а так ли это? 🙂
Я правильно понял — хитрости с хешем нужны потому, что нет конкатенации строк в запросе?
Как всегда, поставил зачёт, почитал, и даже подумал.
нафига козе боян? Что делать с коллизиями?
Очень любопытное использование запроса. Но разве сложение хешей дает новый хеш?
(20 + 40*31) * 3 = (30 + 60 * 31)*2. Получим 6 вхождений «хеша»
Да и число 31 кажется недостаточно большим — символы с кодами большими 31 вносят дополнительную опасность смешивания результатов.
(62 + 10*31) = (31 + 11*31)
Касательно кодов номенклатуры — если использовать префиксы, то «кл0000021» == «Мк0000021».
(5) Alien_job,
>число 31 кажется недостаточно большим
существует объяснение почему именно 31 от Joshua Bloch:
(2) Alien_job, конкатенация могла бы помочь, но до определенного предела. В запросе нельзя сравнивать (группировать) строки длиннее 1024 символа. Поэтому можно будет классифицировать наборы ограниченного количества свойств. А использование хэш-функции таких ограничений не ставит. Кроме того, это всего лишь один из примеров применения данного приема, наверняка найдутся и другие задачи.
(4) minimajack, тут приведено несколько совершенно реальных практических задач, которые быстро и просто решаются данным методом. В данных задачах коллизии не столь страшны. Кроме того, они крайне маловероятны. Ну, крайне редко случайно смешаются два разных набора товаров, получат общий суммарный рейтинг и поднимутся в топ повыше. Или в один класс раз в сто лет смешаются номенклатуры с разными наборами свойств. — Что тут страшного? А для ответственных применений можно разрядность повысить, чтобы сделать вероятность коллизий еще меньше.
(5) Alien_job, формул вычисления хэш-функций очень много. В статье ссылки приведены. Я взял известную, проверенную. Спорить о том, какая лучше, какая хуже бессмысленно, не имея конкретной статистики строк. Если коллизий окажется много, основание степени можно и поменять.
Арифметическое сложение хэшей дает новый хэш, но худшего качества. Поскольку распределение из равномерного становится нормальным, где в средней части вероятность коллизий выше. Например, по сравнению с поразрядным XOR. В данной задаче это снижение качества не кажется существенным.
Вообще говоря, первоначально было реализовано вычисление хэша в виде CRC путем моделирования работы регистра сдвига с обратными связями. Однако быстродействие того метода не удалось довести до приемлемых значений, поскольку нужно учитывать отдельные биты кодов символов. Поэтому взята более простая формула. И если она используется в Java, почему бы не использовать ее в запросе.
(8) на практике это означает буквально лишение премии.
пример явно не самый удачный…да и использование хэширования в 1С8 для реальных нужд в больших объемах не могу представить; тут либо производительность, либо качество подведут.
(1) Diversus, Такой же подход рекомендуют и на курсах Microsoft в баумантке, поэтому он не только 1совский
(8)
А смысл в алгоритме, который может иногда давать неправильные результаты? Данная задача нормально решается кодом, например, собираем сумму кодов номенклатуры в длинную строку, а потом по этой строке группируем документы, чтобы определить какой набор встречается чаще.
Конечно это не отменяет полезность хеширования и тема раскрыта интересная.
(12) Synoecium, давайте сначала оценим вероятность неверного выбора Топ-5 наборов при использовании приведенного запроса.
Пусть есть M действительно разных наборов. Вероятность того, что два из них будет иметь одинаковый хэш, равный сумме хэш-функций кодов номенклатуры, можно оценить числом от (M-1)M/2#k8SjZc9Dxk32/K до (M-1)M/2#k8SjZc9Dxk32, где К — максимальное число товаров в наборе. То есть, если разных наборов 1000, то вероятность будет меньше, чем 999000/4294967296 = 0,000232598.
Далее нужно учесть то, что хэш должен совпасть у наборов, совместная встречаемость которых попадает в Топ-5. Если 5-е место чаще 10-го в два раза (предположим), то, значит, для попадания суммой в Топ-5 по отдельности наборы должны попадать в Топ-10, что дает еще 10/1000*10/1000=1/10000 шансов. Получается, что неверным выбор будет в 2,32 из 100 000 000 (ста миллиона) случаев.
Даже если вы хотите перезаложиться на этот маловероятный расклад (как делают начинающие игроки в преферанс, сильно раздражая более опытных игроков), это не будет иметь практического смысла, так как в жизни более вероятным будет несоответствие выписанных товаров реально отпущенным (пересортица) и ваш суперточный расчет перечеркнется невнимательностью кладовщиков. Возможно, есть люди, которые, выходя из дома, надевают каску, учитывая отличную от нуля вероятность попадания в них метеорита (зная площадь земли и количество долетающих до нее метеоритов, эту вероятность тоже можно посчитать). Уподобляясь этим суперперестраховщикам, для данной задачи можно увеличить разрядность хэш-функции до 64-х или посчитать еще один хэш по другому основанию степенного ряда. Тогда вероятность совпадения хэшей наборов уменьшиться еще примерно в 4 миллиарда раз.
Кажется, этого по любому должно быть достаточно, чтобы доверять сравнению наборов по таким хэшам.
(13) аргументы ваши убедительны, но (говорю только за себя) хочется быть уверенным в своем алгоритме и не держать в голове, что теоретически, он может сбойнуть, хоть и очень редко. Достаточно одного раза, чтобы остался осадок.
(14) Synoecium, (13)
до 15 000 000 (пятнадцать миллионов) значений хеш-функция абсолютна безопасна( с учетом того что коды используются только числовые)
0000…1
0000…2
……..
15000000
коллизий не было
ИМХО использовать для хэширования можно. Использовать в наборах я бы не стал
зы на java(i3)10 мил-нов хэшей(10 символов) расчитывается за 4.5 сек
Хочется только сказать: «ПОБОЛЬШЕ БЫ ТАКИХ АВТОРОВ НА ИНФОСТАРТЕ»
Добавлен Пример 3, показывающий, как можно выбирать из таблиц базы данных необходимое число записей СЛУЧАЙНЫМ образом. Ранее планировал сделать отдельную публикацию на эту тему, но запрос до такой степени прост, что отдельной публикации, кажется, не заслуживает.
Это, опять же, костыль. Нужно добавить Дату.
И, по сути, это Дата дает «случайный порядок», который вы просто пытаетесь «поймать».
Нужно сказать, что это никакой не костыль, а общий принцип построения генераторов случайных чисел. Все они используют какой-либо «источник энтропии». Многие — текущие время. Можно посмотреть таблицу по ссылке:Генератор псевдослучайных чисел . Из таблицы видно, что, например, в Windows в качестве источника энтропии используется: текущее время, размер жёсткого диска, размер свободной памяти, номер процесса и NETBIOS-имя компьютера. Затем на основе этих данных строится хэш-функция MD5, что и дает в итоге случайное число.
В нашем случае о криптографии речь не идет и все чуть попроще: источник энтропии — только текущее время, а хэш-функция — «31».
(19)
Это потому что ответ на «Главный вопрос жизни, Вселенной и всего вообще» уже дан? )))
Сергей, при попытке выполнить в консоли запросов запрос из Примера 3 возникла следующая ошибка:
Microsoft SQL Server Native Client 11.0: The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
HRESULT=80040E57, SQLSrvr: SQLSTATE=22003, state=1, Severity=10, native=535, line=1
Версия СУБД MS SQL 2012… слишком большая точность для даты?
(22) Dach, а параметр &ТекущаяДата не забыли проинициализировать? Он должен иметь тип «дата» и включать время до секунд.
(23) конечно, я же в консоли запросов выполнял запрос.
(24) Dach, очень странно. Вычисляется всего лишь число секунд с 1.01.0001. Это не слишком большое число. Попробуйте тогда взять
Суть не в начальной дате. Главное, чтобы результат выражения при каждом следующем выполнении запроса давал разное число. Но а по-поводу диапазона значений аргументов в этой функции в вашей версии сервера — попробую изучить этот вопрос.
(25) заработало, но очень странно. Все время возвращает одну и ту же выборку, причем состав выборки меняется только если задать другую дату….
(26) Dach, так и было задумано. Энтропия должна быть внесена в запрос извне. В самом запросе нет надежного способа получения непредсказуемых значений. А так получается: перед каждым вызовом определяем текущее время и получаем сколько нужно «случайно-выбранных» элементов справочника. Случайность выбора определяется тем, что момент запуска запроса не предопределен.
И путем нехитрых экспериментов, методом деления пополам была найдена максимальная дата, начиная с которой идет переполнение длины возвращаемого числа:
РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1947, 1, 1) : вот так не работает
РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1948, 1, 1), &ТекущаяДата, СЕКУНДА) : вот так работает
(28) Dach, спасибо, на мой взгляд, так быть не должно. Видимо, это ошибка у 1С. А какая версия платформы?
(27) точно, прошу прощения, невнимательно прочитал описание примера)))
Хм… то есть если знать источник энтропии — то можно формировать какие-надо выборки… Все думают, что случайно, а оно на самом деле…
Знаешь энтропию — владеешь миром! ))))
(29) платформа 8.3.5.1383
(31) Dach, просьба: попробуйте, пожалуйста, вариант вот в таком виде:
Теперь понятен прикладной смысл задачи вообще и ход Ваших рассуждений. То есть наверное была задача получать случайную выборку чего-нибудь. Как обеспечить случайность? Надо иметь источник энтропии и чтобы исходные данные имели уникальный ключ. Тогда надо в статью добавить фразу, что это работает только для исходных данных, где есть такой ключ — в примере это код справочника «Номенклатура». Для регистра сведений это будет уже набор измерений ну и т.д.
(32) РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), &ТекущаяДата, СЕКУНДА) : не работает, та же ошибка
(34) Dach, в общем, понял в чем дело. Разность дат не может быть больше, чем помещается в 32 бита, то есть 2#k8SjZc9Dxk32 = 4294967296, даже меньше 2#k8SjZc9Dxk31 = 2147483648. Тогда получится 18.03.1947 от сегодняшней даты.
Поправил запрос. Будет еще лет шестьдесят работать.
Подтверждаю (22)
HRESULT=80040E57, SQLSrvr: SQLSTATE=22003, state=1, Severity=10, native=535, line=1
1С:Предприятие 8.3 (8.3.5.1517) SQL 2008R2
(36) kasper076, поправил теперь в обоих запросах. Чтобы запрос работал в SQL-базе. Нужно было в функции
исправить первый аргумент так
Тогда разность дат, выраженная в числе секунд будет укладываться в величину, представимую четырехбайтным целым со знаком и запрос будет работать и в SQL-варианте.
На самом деле именно разность дат вовсе и не обязательно использовать. В другом варианте того же запроса вместо всего выражения
можно ввести в запрос ОДНО случайное число, полученное объектом ГСЧ.
Спасибо
(10) Ну одно из применений хеширования в 1С — это выборочная регистрация объектов на узлах планов обменов.
Например, у меня есть поле контрагент и номенклатура — при изменении которых есть смысл ставить на регистрацию объект. Сейчас в БСП это работает формированием текста запроса, а насколько удобней такие вещи делать через хеширование? И по производительности, и по качеству это намного лучше того, что имеем сейчас.