<?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='\
Спасибо за статью, интересно.
Вот люблю я статьи маэстро! Они делают меня лучше
Определить автора можно по заголовку, даже не заглядывая на страницу.
Бесподобно!
Предполагаю, что у автора фундаментальное мат. образование, мало таких в мире 1с
(5) 1cmax, нет, образование у меня техническое
Ничего не понял, но, на всякий случай, плюсанул )))))
А банальный обход этой таблицы в цикле решает эту задачу медленнее?
(8) qwinter, думаю, что
вне запроса решает эту задачу быстрее. Даже с учетом необходимости сортировки. СКД или оконные функции в других СУБД тоже могут помочь. Однако, если интервалы нужны в самом запросе для последующей обработки, то выгоднее использовать этот подход.
(9) запрос конечно веселый)) 900 строчек)))
Хороший новый способ взгляда на проблему.
Кажется, нашел еще одно важное применение данного алгоритма. Это поиск пропущенных номеров! Задача актуальна, если судить по дискуссии вИнтересная задачка для решения на SQL (Найти в диапазоне номеров пропуск) .
Приведенный в статье алгоритм позволит найти все пропуски как интервалы между номерами, которые больше, чем 1. И сделает это быстро!
Это если стоит задача найти все пропуски. Если нужно найти только первый, то данный алгоритм будет не нужен. Подойдет запрос с условием
Но в нем тоже могут быть подводные камни, связанные с возможным использованием NESTED LOOP для этой проверки. Это можно обойти через прием с группировкой.
В свое время пришлось разбираться с интервалами.
ссылку на свои заметки ,- возможно, кому-то пригодится.
Стандартные задачи поиска свободных мест размещения, подбора интервалов оказания услуг, графики, расписания и т.п.
Оставлю здесь
Самое вкусное (алгоритмы свертки и соединения), правда, там осталось за кадром (руки не дошли). Но основные понятия даны.
Дмитрий Малюгин
(13) kint, большое спасибо, интересно
Добрый день.
Спасибо, за такой удивительный запрос.
Ощущается магия какая-то.
Применил данный метод на поиск свободных штрихкодов.
Правильно понимаю, что 32 тура позволяет искать до того, пока разница секунд не будет больше 4294512000?
Т.е. в моем случае пока штрихкоды от 0 до 4294512000?
Сформулирую по другому вопрос:
если ищу свободные штрихкоды в интервале
от 210000000000
до 210001000000
это 1 миллион штрихкодов, будет ли достаточно 32 туров?
(15) BlizD, все правильно, 32 тура это 4 294 967 296 секунд — больше четырех миллиардов.
Поиск пропусков в нумерации документов запросом , заменив в запросе выборку номеров документов на выборку штрих-кодов. В отчете из той статьи сразу решается задача преобразования строки в число в запросе. Для 12-ти значного штрих-кода понадобится 40 туров.
А для миллиона штрих-кодов будет достаточно 20-ти туров, так как 2#k8SjZc9Dxk20 = 1 048 576 > 1 000 000.
В принципе, если штрих-код у вас в базе, то это, скорее всего, строка. Поэтому для поиска пропущенных штрих-кодов можно воспользоваться отчетом из статьи
(16)
Спасибо, значит все правильно сделал.
Да, штрих-код у нас это строка и преобразование в число делал из той статьи , что указали:D
Ищу в заданном интервале, поэтому остановлюсь на 32 турах.
(13) Круто. Не могли бы, Вы, накидать источников (литературы, ссылок)? Особенно интересно по темам в разделе «К описанию» 🙂
Мне надо узнать длительность каждого действия. Отрезков может быть несколько десятков тысяч поэтому выбрал этот алгоритм, но меня отчего-то результат не верный.
(19) Если отчет на СКД, попробуйте в последний запрос добавить в принципе ненужные поля НижняяГраница, ВерхняяГраница. СКД может исключать эти поля из промежуточных запросов, что приводит к таким казусам.
Нет, не на СКД — пока только запрос.
(21) Ну тогда используемый текст запроса пришлите. Там точно все каскады? Многоточие не забыли запросами заменить? И исходную таблицу, которая в (19), если можно.
Может быть эта задача так не решается? Может надо по-другому делать? Мне кажется перебором по ТЗ будет медленно — мне надо три таких набора разобрать
(23) Формально все верно, запрос вроде бы такой, какой нужно, но теперь непонятно, в чем вы видите ошибку в (19).
Судя по запросу, определяются интервалы между отметками времени, соответствующими одинаковым действиям.
В (19) четыре отметки, отмеченные «Пусто» и четыре отметки «ХХШ».
С моей точки зрения, в линейке «Пусто» верно определены три интервала:
5:01 — 6:02 — 6:13 — 7:02,
а в линейке ХХШ определены три интервала:
6:11 — 7:13 — 7:26 — 8:02.
Возможно, задача была в том (тут требуется догадываться, поскольку содержательная сторона в вопросе, к сожалению, опущена), чтобы найти интервалы между ближайшими отметками времени без учета действий, а затем пометить интервалы действиями, соответствующими его первой точке.
Но тогда нужно во всех запросах группировку по действиям убрать, чтобы найти элементарные интервалы, а уже потом маркировать интервалы действиями.
(24) Думаю, решается. Решение нужно всего лишь немного подправить, вы на верном пути.
Мне бы хотелось получить следующее:
Это данные с датчиков в автомобиле — двигатель работает/не работает, машина — движется/стоит. Соответственно надо посчитать, сколько времени ехала, сколько стояла со включенным двигателем. Плюс разные другие датчики — уровни топлива в разных баках, работа второго двигателя. Проблема в том, что разные наборы датчиков посылают сигналы в разное время. По-началу я группировал всё до минуты, собирал всё в одну таблицу и её обходил. Но на нескольких сутках округление даёт, в таком случае, большую погрешность. Если не объединять до минут, то надо делать три таких запроса, три обхода — будет долго.
(28) Теперь более-менее понятно.
https://infostart.ru/public/201526/ , второй попроще — упрощенная версия https://infostart.ru/public/551583/ (пока в черновиках).
Тогда действительно метод не вполне подходящий (одного его недостаточно).
Он предназначен для поиска интервалов между событиями, а вам еще нужно будет найти: в какие интервалы постоянства значений наборов датчиков входит этот интервал.
То есть соединить полученную таблицу интервалов с таблицей каждого набора датчиков по условию Tдатчика <= НачалоИнтервала, найти максимум Tдатчика с группировкой по индексу интервала, а затем соединить полученный максимум с таблицами, чтобы определить состояние набора датчиков.
Это задача множественного среза последних.
Проблемой будет поиск максимума для каждого минимального интервала.
Это в целом затратная операция, так как ее трудоемкость NхM/2, где N — количество минимальных интервалов, а М — число значений в таблице значений наборов датчиков.
Тут есть пара довольно головоломных подходов.
Один на основе
Но прежде чем их применять, нужно замерить время решения через ТЗ.
Если оно будет удовлетворительным, ломать голову не стоит.
(0)
ВЫБРАТЬ
Слева.Дата КАК НачалоИнтервала,
МИНИМУМ(Справа.Дата) КАК КонецИнтервала
ИЗ
Даты КАК Слева
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Даты КАК Справа
ПО Слева.Дата < Справа.Дата
СГРУППИРОВАТЬ ПО
Слева.Дата
Угу, так и делаем. Но такой запрос чреват ошибкой, если вдруг попадутся два смежных интервала (конец одного равен началу другого).
(30) Этого замечания я не понял.
То есть если в исходной таблице есть отметки времени 5:01; 6:02; 6:13; 7:02, то должны получиться интервалы:[5:01, 6:02], [6:02, 6:13], [6:13, 7:02]. Задача, в которой интервалы имеют нулевую длину (когда отметки повторяются), не рассматривается, поскольку, как минимум, требует доопределения (еще одного поля для упорядочивания). Можно считать, что отметки времени выбираются запросом ВЫБРАТЬ РАЗЛИЧНЫЕ, что исключает их дублирование.
(31) А, я невнимательно посмотрел запрос. У меня другая таблица: два поля — Начало и Конец. У Вас одно поле — Дата. И задача у меня немного другая: я определяю запросом промежутки между интервалами. Моя ошибка, извиняюсь.
Спрошу тут, задача похожая.
Исходная таблица c пропущенным интервалом, таких может быть несколько.
t1-t2
t2-t3
t4-t5
Как запросом добавить пропущенные интервалы?
Задача из ЗУП, когда на период задано что-то плановое, а внутри идут временные перемещения. Промежутки между временными нужно заполнить основным.
(33) Тут требуется уточнение. Если отрезки не пересекаются, то все просто. Для определения пропусков нужно соединить таблицу интервалов саму с собой примерно так:
Показать
Еще можно пронумеровать все крайние точки интервалов подряд и соединить соседние четные с нечетными. Так делают в SQL, где есть функция RowNumber(). Задача называетсяGaps and Islands Problem .
Если интервалы в исходной таблице могут пересекаться, то тут два пути:
Минимализмы .
Первый: Соединить таблицу с производственным календарем, чтобы определить дни, не входящий ни в один интервал, а затем сгруппировать соседние дни, чтобы получить сами интервалы. По принципу задачи 14 из
Второй: посчитать нарастающий итог, где начало интервала делает +1, а конец -1. Тогда пропуски будут соответствовать интервалам, где нарастающий итог нулевой.
Все эти решения годятся для случаев, где интервалов до тысячи. Если их больше, то, чтобы не столкнуться с квадратичным ростом времени выполнения запросов, требуется применять специальные методы типа описанного в этой статье и в статьеБаттерфляй — метод быстрого расчета нарастающего итога в запросе .
Есть ли какое-нибудь математическое «решение», у следующей задачи:
Дано:
в телесистему (виалон) можно сделать запрос по одному/нескольким объектам, но строго за один интервал;
в системе 1С у объектов интервалы по большому счету разные (дата выезда и убытия по ПЛ);
количество запросов в телесистему лимитировано;
ответ из телесистемы можно получить либо в целом за интервал, либо он разбит на подинтервалы произвольным образом неодинаково по объектам (работает / не работает двигатель, двигается авто / стоит). Подинтервалы при этом соприкасаются
Необходимо как-то минимизировать количество запросов в телесистему, и при этом минимизировать запись в системе 1С
Задача кажется интересной, нужной, сложной, но данных для выбора способа решения недостаточно.
Нужно знать структуру полей таблицы, возвращаемой по запросу из Виалон, метод определения стоимости запроса (стоимость определяется или числом запросов, числом строк в запросе, числом объектов и прочее), а также структуру регистра для хранения полученных данных в 1С.
В 1С предполагает примерно такая структура (упрощенно), РС: измерения — ИдОбъекта (строка), ДатаУбыт, ДатаПриб (даты со временем), ВидКэша (по сути вид таблицы из виалон), ресурсы — моточасы, пробег, время, начальное и конечное положение (строка) и другие числовые.
Тут еще думаю добавить измерение Сутки (чтобы можно было наборы писать посуточно), в итоге записей по объекту может быть 4-100 за сутки.
Сколько объектов может быть запрошено в запросе максимально, пока не знаю, но ограничения точно есть, и их полно: если за 5 минут запрос не выполняется, он прерывается, поэтому например за час данные можно получить сразу по всем объектам, а за сутки, уже надо снижать количество объектов (пробовал запрашивать по 100 объектам, за сутки выдавало результат). Передать в запрос тоже можно ограниченное число объектов для запроса, но как уже отметил, максимальное значение не знаю. Строк возвращается всегда разное количество, ответ (json) разбит на 4 таблицы, в 2х из них есть несоприкасающиеся интервалы, а в двух других, просто определенные моменты времени (даты заправок, и т.п.).
Из ограничений еще такое важное (ограничения указаны на сайте виалона, не знаю, можно ли тут ссылки приводить): по одному пользователю можно запросить за 5 минут не более 200 запросов (вроде как это по всем сессиям пользователя, и, не совсем понял, ранее у них не было указано, что это запросы определенного типа, а сейчас уточнено, что report/exec_report . Этот момент пока не тестировал, но в целом, на один такой запрос, приходится 6-7 других
Объектов может быть порядка 1200-1500 (может и больше, но у меня в имеющихся тестовых базах примерно столько)
Если этой информации недостаточно, то к сожалению, на текущей момент другой не владею
Как предполагал решить задачу сам: сначала загрузить за сутки по группам объектов (скажем по 100 штук), а затем, если, границы интервала по объекту «попадают» в загруженные подинтервалы, то эти подинтервалы разделить исходя из нужных границ.
В принципе такую реализацию сейчас и буду доделывать, но первая версия показала, что выполняется это очень долго (порядка 10 часов), с учетом ночных «перегрузок» сервера 1С неприемлемо, задача, сократить хотя бы до 5 часов.
Думаю тут выигрыш дадут запись наборами за сутки, и сейчас у меня разбивка поинтервалов идет по каждой из «таблиц» виалона (т.е. 2 таблицы на 2 границы — до 4х запросов в виалон), это сокращу строго на 1 запрос. В принципе если смогу сократить до 5 часов загрузку, то задача решена, и наверное переделывать не буду.
Пробовал еще такую версию: разбивал сутки на подпериоды по всем границам ПЛ и запрашивал сразу по всем объектам, но в итоге загрузка также была в пределах 10 часов, и обеспечить «посуточную» загрузку оказалось сложно, в случае ошибки при загрузке (или не успел придумать вариант). Тут наверное как то надо было разделять с учетом ИД объектов для каждой границы, но я не сообразил, как
Сергей, спасибо за участие.
В итоге выяснил, что самый большой проигрыш почти половина времени уходила на разбор «самописными» процедурами JSON. Переделал на платформенное, и еще некоторые моменты поправил, в итоге добился 3х часов.
Но если все же будет объяснения, как решать такие задачи «математически», было бы очень интересно, или хотя бы намек, что это за тип задачи, чтобы самому попытаться разобраться.
А еще хотел бы узнать, есть ли какая-то возможность «сгруппировать» в запросе по интервалам ? Не по периодам, а именно, что интервалы в записях:
1 Дата1, Дата2
2 Дата1, Дата3
3 Дата1, Дата3
В итоге записи 2 и 3 — это «группа интервалов»
потуплю…)
можете помочь с простым скд ?
надо отобрать реализации группируя их по 15 мин, условно начиная с 9:00:00?