Выборка в запросе из периодического регистра сведений данных на дату из строки запроса




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

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

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

<?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='\

48 Comments

  1. ZyZer

    А почему именно вложенный запрос??? Почему не временная таблица? Это и отлаживать удобнее и более понятно для глаз. Честно говоря, вообще коробит, когда такие промежуточные запросы называют «ВложенныйЗапрос», неужели нельзя дать понятное наименование?

    Reply
  2. Ivon

    (2). Я не изобретатель велосипеда. Просто не все учатся на курсах и читают нужные книги. Много самоучек с большим стажем работы и не факт, что они до этого дошли. Я показал как это решается. И я уверен, что найдутся те, кому это поможет в решении своей задачи. Я тоже самоучка и у меня нет сертификатов. На ресурсе я тоже вижу статьи и файлы, которые для меня не представляют ничего сложного и интересного, но тем не менее людям они помогают. И что, им тоже минусы?

    (3). Ну я рассматривал вариант, когда все делается в одном запросе, а не в двух и более. Временная таблица подразумевает использование менеджера временных таблиц и программной обработки результата запроса и еще как минимум 1 запрос.

    Reply
  3. Ivon

    (1). Спасибо за понимание.

    Reply
  4. ZyZer

    Менеджер временных таблиц можно и не использовать — все организовать в одном запросе, например как-то так(как красиво вставить код в ответе не знаю):

    ВЫБРАТЬ

    Договор.Сумма КАК Сумма,

    Договор.Ссылка КАК Ссылка,

    МАКСИМУМ(Руководители.Период) КАК Период,

    Договор.Сотрудник КАК Сотрудник

    ПОМЕСТИТЬ РуководителиДоговоров

    ИЗ

    Документ.Договор КАК Договор

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Руководители КАК Руководители

    ПО Договор.Сотрудник = Руководители.Сотрудник

    И Договор.Дата >= Руководители.Период

    ГДЕ

    Договор.Дата МЕЖДУ &Дата1 И &Дата2

    СГРУППИРОВАТЬ ПО

    Договор.Сумма,

    Договор.Ссылка,

    Договор.Сотрудник

    ;

    ВЫБРАТЬ

    РуководителиДоговоров.Ссылка КАК Договор,

    РуководителиДоговоров.Сумма,

    Руководители.Руководитель

    ИЗ

    РуководителиДоговоров КАК РуководителиДоговоров

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Руководители КАК Руководители

    ПО РуководителиДоговоров.Период = Руководители.Период

    И РуководителиДоговоров.Сотрудник = Руководители.Сотрудник

    Reply
  5. hame1e00n

    Статья безусловно полезная, я думаю есть начинающие программисты на 1С, которые об этом не знают. В любом случае, именно подобные статьи и делают инфостарт таким ценным ресурсом — мы делимся опытом и сами чему-то учимся. Не пойму, зачем ставить минус, если ты знаешь об этом приеме.

    Короче автору однозначно +

    Reply
  6. Ivon

    (6). Согласен. Работает. Еще 1 вариант. Наберу рейтинг 30, поставлю +.

    Reply
  7. Ivon

    (7). Спасибо.

    Reply
  8. Ish_2

    Рассмотрим первый пример из статьи :

    ***************************************

    ВЫБРАТЬ

    Договор.Ссылка КАК Договор,

    СУММА(Договор.Сумма) КАК Сумма,

    РуководителиСрезПоследних.Руководитель

    ИЗ

    Документ.Договор КАК Договор

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Руководители.СрезПоследних(&Дата2, ) КАК РуководителиСрезПоследних

    ПО Договор.Сотрудник = РуководителиСрезПоследних.Сотрудник

    ГДЕ

    Договор.Дата МЕЖДУ &Дата1 И &Дата2

    СГРУППИРОВАТЬ ПО

    РуководителиСрезПоследних.Руководитель,

    Договор.Ссылка

    ******************************************

    Язык запросов — язык декларативный , т.е. не определяющий последовательность действий при выполнении платформой запроса.

    В какой последовательности ,на взгляд автор, будет происходить исполнение

    запроса :

    1. Вначале будет выполнено левое соединение всей (!) таблицы Договоров с регистром сведений и лишь потом фильтрация полученной промежуточной таблицы по условию

    «ГДЕ

    Договор.Дата МЕЖДУ &Дата1 И &Дата2″».

    2. Или вначале будет фильтрация талицы Договоров по условию «ГДЕ …»

    и лишь затем левое соединение полученной промежуточной таблицы с регистром сведений.

    Итак , п.1 или п.2 ?

    Reply
  9. ZyZer

    (10) О том, как исполняется в файловом варианте — не знаю, но у движка MSSQL вроде как хватает сообразительности пойти по 2 варианту. Подцепитесь профайлером и посмотрите план исполнения запроса.

    Reply
  10. Magister

    Я давно уже про это читал на Мисте:

    http://www.kb.mista.ru/article.php?id=92



    А какой собственно выигрыш при использовании временных таблиц? Мне например они как-то не по душе, вложенные запросы понятнее.

    Reply
  11. Ish_2

    (11) Спасибо за совет. Но подождем ответа автора для для файлового и SQL вариантов.

    Reply
  12. Ivon

    (10). Честно говоря не знаю, да и абсолютно нет разницы для конечного результата.

    Reply
  13. ZyZer

    (12) Ну про вложенные запросы — это на вкус и цвет, как говорится, но мне удобнее видеть выборки отдельно. Да и если выборки сложные и требующие частого вычисления одного и того-же в нескольких запросах — менеджер временных таблиц может здраво ускорить выборки.

    Reply
  14. s_a_r_u_m_a_n

    За идею — плюс. Помню, сам парился на эту тему и решил проблему аналогичным способом )) будет полезна многим

    Reply
  15. afanasko

    (12)

    Во временных таблицах можно строить индекс по колонкам.

    Reply
  16. Ish_2

    (14) Конечно нет разницы для конечного результата.

    Разница будет во времени исполнения . По п. 1 оно может быть очень значительным.

    Чтобы исключить п.1 и не надеяться на оптимизатор запроса при файловом

    или SQL-варианте в указанном тексте лучше применить временную таблицу или вложенный запрос.

    Насколько мне известно , «1с» не публиковала описание оптимизатора запроса в файловом варианте.

    Reply
  17. erem

    в Укр. типовом ЗУП полно таких конструкций. Метод действительно давно уже используется — «Срез последних на разные периоды», а плюс за описание для молодежи…

    Reply
  18. Ivon

    (18).Ну, как вариант, можно сделать вначале выборку нужных данных во вложенном запросе. Так будет явно понятно в какой последовательности происходит выборка, но приведенный тобой запрос (10) все-равно является неверным для задачи, так как выдает неправильный результат, поэтому какой смысл его использовать?…

    Reply
  19. Ish_2

    (20) Пример взят из описания текущей темы . И в последующем примере запроса в описании темы — та же ошибка при построении запроса.

    Применение в данном случае вложенного запроса или временной таблицы —

    нужно рассматривать не как один из вариантов ,

    а как оптимальный вариант запроса.

    Подробнее прочитать про оптимальность и неоптимальность запроса

    можно в статье Павла Чистова «Пакетные запросы» из рубрики «1с рекомендует».

    Reply
  20. Ivon

    (21). Будет время — обязательно сделаю базу с количеством записей 20 000 на каждый объект конфигурации и проверю разницу времени выполнения запроса. На текущий момент могу сказать, что выполнение сложного запроса и построение отчета за 2 секунды целиком устраивает моих пользователей.

    Reply
  21. Ish_2

    (22) Чтобы картина была более полной , возможно будет интересно почитать

    и сторонников противоположной точки зрения

    http://infostart.ru/blogs/1179/

    Reply
  22. lsp71

    + за просвещение самоучек (тоже была такая проблема).

    Reply
  23. Ivon

    (23). Вообще-то статья была на тему составления запроса для получения правильного результата, а не о оптимизации и скорости выполнения запросов. Думаю, что дальше развивать полемику на эту тему нет смысла. Для этого скорее всего и была создана тема, ссылку на которую ты указал.

    Reply
  24. ninch

    Особо еще не разобрался в тонкостях, но были такие же ситуации, возможно тогда бы такое решение мне бы и помогло:))) Спасибо за ликбез

    Reply
  25. DoReMi

    Несколько странным кажется, что люди не умеют пользоваться поиском и поисковыми машинами.

    Потрачено время на то, что уже придумано другими людьми гораздо раньше. Я бы не знал куда спрятаться от стыда.

    Reply
  26. Ivon

    Добавил в статью вариант запроса с временной таблицей от ZyZer из поста 6.

    Reply
  27. Ivon

    (27). Не надо обалдевать от собственной крутости. Бывает, что просто не видишь даже вариантов решения вопроса. Как в фильме: видишь суслика? Нет. А ведь он есть. Кто-то (как ты, например) посещал немеряно курсов и знает про 1С все (ну или думает, что знает), а кто-то нигде не учился. Я например, до 1С программировал на 3 языках: Perl, VB и C#. А СрезПоследних так забивает голову, что сразу и не найдешь решение, если не вспомнишь былую практику или, как в твоем случае, курсы.

    Reply
  28. DoReMi

    (29) про курсы анекдот какой-то, я слесарь-сборщик радиоаппаратуры и консерваториев не кончал, однако когда проблема встала с необходимостью получения данных в запросе на дату документа — нашел на мисте описалово метода и стал пользоваться, и это не моя крутость, а ваша пафосность в представлении этого баяна немного меня зацепила.

    Reply
  29. Ivon

    (30). Ну, никто же не мешал тебе сделать статью здесь. Я разместил статью потому, что дошел до этого своим умом, когда знакомые считали это сложноразрешимым. Более того, некоторое время сам думал, что это нереально сделать в одном запросе. Я же не написал, что это открытие века.

    Reply
  30. DoReMi

    (31) Ндя… А просто погуглить «запрос срез последних на дату документа»?

    Reply
  31. DoReMi

    Надо завязывать конечно с этим спором, я не размещаю статей, я не учусь на курсах, я просто прежде чем написать «я открыл Америку» пытаюсь найти информацию, не открывал ли кто-то её уже раньше. И вам советую. Во-1х экономит время сильно, во-2х не попадёте в глупое положение.

    Reply
  32. Ivon

    (33). Во первых, я не писал, что «я открыл Америку», а во вторых, как показало время, статья оказалась полезной другим. А то, что статей не пишете — так это вам не в плюс. Знаниями и опытом надо делиться.

    Reply
  33. logarifm

    Да но в этом методе есть и отрицательные стороны. На больших таблицах, будут тормоза. А еще если это связано с регистром накопления, а что если необходимо получать остатки на дату документов, а регистр к примеру партий товаров… Вот этот способ не сработает. То есть оно работать будет но ужастно долго!!!!!!

    Reply
  34. Ivon

    (35). Естественно, запрос будет отрабатывать дольше. Но он будет работать и выдавать правильный (!) результат. Если у Вас есть решение лучше — пожалуйста, предложите. Во всяком случае это решение будет работать гораздо быстрее, чем если обрабатывать эти вещи не в запросе, а в коде.

    Reply
  35. logarifm

    (36) Смотря для каких задач… Это хорошее решение и правильно что выложили его здесь… Просто не для всех оно подходит. Например мне необходимо было делать выборку по заказам покупателей и на момент заказа смотреть стоимость партии в регистре накопления «ПартииТоваров», вот этот вариант уже не подходит… Постоянный перерасчет регистра на всю номенклатуру…

    Тогда приходится применять обходную хитрость что вы и сказали кодом… Делаю сперва один запрос вынимая заказы и номенклатуру в обходе результата запроса вызываю уже второй запрос, которому даю номенклатуру на которую он должен расчитать данные и дату.

    Не спорю это не гуманно, но пришлось и так обходить!

    Reply
  36. Ivon

    (37). А вы не пробовали сравнить скорость выполнения обработки кодом и сложным запросом? Я думаю, что на среднем компьютере сложный запрос будет выполняться быстрее, чем обработка кодом. Сейчас у меня отчет на запросе в 1642 строки отрабатывает порядка 20 секунд. Я даже не представляю, сколько он отрабатывал, если бы обработки были в коде. Ведь код обрабатывается на клиентской машине, а весь запрос на сервере, который гораздо производительнее клиентской машины.

    Reply
  37. logarifm

    (38) Опять же спорно… Сделайте так как я говорю и вы увидите в чем прикол, структура хранения регистров сведений и регистров накоплений совершенно другая. И в запросе указывая дату расчета на каждый документ происходит постоянный перерасчет данных…

    А насчет кодом у клиента, необязательно, можно клиенту отдать таблицу значений которая отработана на сервере (ну это касательно 8.2, хотя 8.1 можно построить на уровне серверных модулей схожее выполнение).

    Reply
  38. logarifm

    И все же яне опровергаю вашу статью и ставлю +1. Я сам пользовался и не однократно срезом регистра сведений на «динамические даты» и это пролетало, а вот с регистром накоплений к примеру «Партии товаров» можете представить себе объемность данной таблицы, долго… Так как нету фильтра на номенклатуру в этом беда, он рассчитывает таблицу в целом…. Я просто поднял свою тему так как пришлось совершенно недавно с этим столкнуться: Поднимал этот вопрос на мисте

    И выкладываю этот материал как примечание чтобы взять во внимание. Спсасибо за понимание, а автору плюс за труды!

    Reply
  39. Ivon

    (40) Честно говоря, я стараюсь не использовать внутренние механизмы типа «Срез». Механизм «Обороты» так же в последнее время в основном не вызывается, а вместо него пишется вложенный запрос. Как показала практика, самописные выборки гораздо гибче встроенных механизмов. С «Остатками» мне почти не приходится работать, но, думаю, если придется, можно и это реализовать.

    Reply
  40. kolpak_mp3

    Поражаюсь. Одна критика. Я вот вчера только узнал про вложенные запросы. Для начинающих полезная статья. Автору +

    Reply
  41. hren

    (32) из-за таких как ты, все запросы в гугл отправляют тебя на страницу, где советуют обратно поискать в гугле

    Reply
  42. Valerich

    (32). Если уж советуешь погуглить, то приведи сразу ссылку, потому как (43) 100% прав

    Reply
  43. DoReMi

    (43) и (44) вам почти год понадобился чтобы найти решение в гугле? 🙂 вот ссылка http://kb.mista.ru/article.php?id=92 первая редакция от 16.02.06

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

    Reply
  44. Valerich

    (45) я не рассуждаю о свежести темы. Просто (43) прав в принципе безотносительно темы. Очень часто ищешь информацию (ЛЮБУЮ), а натыкаешся только на своеты погуглить, спросить яндекс и т.п., что не ускоряет поиск нужной информации, потому как эти советы гораздо свежее по времени самой информации.

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

    С уважением. Ничего личного.

    Reply
  45. A_kryl
    Для начала нам необходимо получить всех руководителей, период записи которых меньше или равен дате договора. Потом все поля, кроме периода сделать группировочными, а период взять максимальный.

    Данная фраза не верна — нужно сделать группировку по всем измерениям регистра и по периоду — чтоб в выборке однозначно получить его ресурсы, и вообще относительно http://www.kb.mista.ru/article.php?id=92 пример не доделан, выборкой с группировкой мы находим конкретную запись, связываем по измерениям еще раз с регистром сведений и получаем ресурс(ы). А потом уже они выводятся в окончательный запрос. В качестве примера — регистр цен, если я сделаю группировку по ресурсу цена — никакой максимум не сработает, а так как в группировке участвуют все поля запроса 🙁

    Reply
  46. mailrum2004

    Спасибо автору за пример использования хоть и известного, но полезного подхода в работе с периодическими регистрами сведений. Иногда помишь, что такое возможно, но не помнишь как. Такие статьи помогают отыскать забытые или потерянные знания.

    Reply
  47. ccserg

    (47) A_kryl,

    видимо столкнулся с такой проблеммой , не работает максимум , не понимаю , как решить ?

    у меня в запросе два регистра сведений соединяются по Периоду

    Reply
  48. valeriy-vm

    Рабочий пример для ЗУП 2.5

    ВЫБРАТЬ

    СУММА(ВЫБОР

    КОГДА РаботникиОрганизаций.ЗанимаемыхСтавок > 0

    ТОГДА 1

    ИНАЧЕ -1

    КОНЕЦ) КАК Количество,

    табПериодов.ДатаПериод КАК ДатаПериод

    ИЗ

    (ВЫБРАТЬ

    КОНЕЦПЕРИОДА(КурсыВалют.Период, МЕСЯЦ) КАК ДатаПериод

    ИЗ

    РегистрСведений.КурсыВалют КАК КурсыВалют

    ГДЕ

    КурсыВалют.Период МЕЖДУ &НачПериода И &КонПериода

    СГРУППИРОВАТЬ ПО

    КОНЕЦПЕРИОДА(КурсыВалют.Период, МЕСЯЦ)) КАК табПериодов

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций

    ПО табПериодов.ДатаПериод >= РаботникиОрганизаций.Период

    ГДЕ

    НЕ РаботникиОрганизаций.ПричинаИзмененияСостояния = ЗНАЧЕНИЕ(Перечисление.ПричиныИзмененияСостояния.Перемещение)

    СГРУППИРОВАТЬ ПО

    табПериодов.ДатаПериод

    УПОРЯДОЧИТЬ ПО

    ДатаПериод

    Reply

Leave a Comment

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