Срез последних на каждую дату в СКД и в запросе




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

76 Comments

  1. Ish_2

    Мелкие замечания.

    1. На мой взгляд , лучше говорить «второй запрос в пакете» , а не » второй подзапрос».

    2. Как-то надо обозначить опасность применения в запросе соединения по неравенству.

    При розничной торговле с гибкой политикой цен пользователь может быть сильно разочарован таким отчетом. «В среднем » более надежно отсечь период до «Даты начала» и вначале использовать соединение с виртуальной таблицей СрезПоследних и получить цены на «дату начала» для каждой номенклатуры. А затем уже делать соединение по неравенству с регистром , используя условие для записей регистра «Где Период >= &ДатаНачала»

    Reply
  2. _also

    1. Спасибо, поправил

    2. Ну все таки отчет приведенный в статье не имеет более общий характер. Соглашусь с Вашим мнением, но все же это уже относиться к реальным задачам. Статья — просто пример 😉

    Reply
  3. tormozit

    Метод, примененный в СКД очень неэффективен, т.е. будет работать значительно медленнее, чем пакете/запросе.

    Reply
  4. director04

    Спасибо, очень интересные решения. Кстати, в одной из задач на экзамене по Бух есть именно такая задача по построению отчета.

    Находил иные решения на Мисте, но против данных решений — они просто монстры.

    Красиво и просто

    Reply
  5. RailMen

    Реализация всевдо среза первых и последних записей на каждый день периода для расчета среднесписочной численности: http://infostart.ru/public/77462/

    Reply
  6. _also

    (3) в СКД можно добиться некоторых плюсов, если некоторые данные не нужны. У них можно установить флаги ограничений, и СКД не будет их получать. А так согласен в подавляющем большинстве случаев запрос эффективнее.

    Reply
  7. Ivon

    Вот тут http://infostart.ru/public/21181/ моя статья по той же теме.

    Reply
  8. I_G_O_R

    Запросы это хорошо , обожаю запросы 🙂 , вот вам еще один вариант, может кто сможет еще короче написать:

    ВЫБРАТЬ
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура,
    Продажи.КоличествоОборот КАК Количество,
    Продажи.СтоимостьОборот КАК Стоимость,
    ЦеныНоменклатуры.Цена
    ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ПО Продажи.Номенклатура = ЦеныНоменклатуры.Номенклатура
    И (ЦеныНоменклатуры.ТипЦен = &ТипЦен)
    И (ЦеныНоменклатуры.Период В
    (ВЫБРАТЬ ПЕРВЫЕ 1
    Цены.Период
    ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК Цены
    ГДЕ
    Цены.Период <= Продажи.Период
    И Цены.Номенклатура = Продажи.Номенклатура
    И Цены.ТипЦен = &ТипЦен
    УПОРЯДОЧИТЬ ПО
    Цены.Период УБЫВ))

    Показать

    Reply
  9. I_G_O_R

    Кстати во втором запросе есть ошибки и недочеты;

    1) в первом запросе данные группировать НЕ надо! т.к. данные из виртуальной таблицы уже сгруппированы

    2) во втором запросе не учитывается типцен, поэтому он выдаст направильный результат, если есть несколько типов цен

    3) в третьем запросе условие на типцен должно быть в соединении а не в условии, поэтому третий запрос вернет только продажи той номенклатуры, для который установлена цена &ТипЦен

    Reply
  10. Ish_2

    (10) Проверить не могу. Навскидку :

    Выглядит симпатично. Могу предположить , что работает быстрее ,чем пример в статье.

    И думаю, вот почему : твой запрос не требует создания дополнительных временных индексов , а использует уже существующие индексы регистра. Насколько я понимаю, все выражения в условии соединения составлены так , что не требуют «дополнительных» усилий от оптимизатора запроса. Ну , а сам то ты сравнивал по быстродействию свой запрос и запрос из статьи ?

    Reply
  11. Ish_2

    (9) ух.. глазастый какой.

    Смотри (10).

    Reply
  12. I_G_O_R

    (11) сравнивал, но на небольшой базе, использовал модифицированный запрос из (0), убрал АВТОУПОРЯДОЧИВАНИЕ и исправил ошибки, в итоге мой запрос совсем чуть-чуть быстрее, а если добавить АВТОУПОРЯДОЧИВАНИЕ, то мой запрос заметно проигрывает, прямо даже на глаз видно, не знаю с чем это связано. Буду на работе попробую, если будет время, на большой базе. Есть у нас клиент, у которого очень много типов цен, интересно на такой базе проверить. Я думаю, что в такой базе, мой запрос может показать лучшие результаты, из-за того что в запросе из (0) второй запрос по идее должен выполнятся долго, т.к. это та же проблема что и с нарастающими итогами.

    Reply
  13. Ish_2

    (12) Ага. Интересно проверить и сравнить оптимальный, исправленный запрос из (0) и твой. Причем в твоем запросе , та же проблема что и с нарастающими итогами.

    Если таблицу регистра цен увеличить в 2 раза то длительность твоего запроса , думаю, увеличится как минимум в 3 раза.

    И вот еще что . Как и в (0) нужно , на мой взгляд, отсечь период до «Даты начала» сделав вначале соединение со срезом последних регистра цен.

    Reply
  14. Ish_2

    (12) +13 Ведь твой вложенный запрос в (8) выполнится столько раз — сколько записей в регистре ЦеныНоменклатуры (если продажи анализируются для всей номенклатуры и без ограничения периода). Еще раз повторяю — выглядит симпатично , но нужно проверять.

    С другой стороны, если бы поле выборки языка запроса 1с могло быть запросом (как во всех диалектах SQL), то :

    ВЫБРАТЬ
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура,
    Продажи.КоличествоОборот КАК Количество,
    Продажи.СтоимостьОборот КАК Стоимость,
    (ВЫБРАТЬ ПЕРВЫЕ 1
    Цены.Цена
    ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК Цены
    ГДЕ
    Цены.Период <= Продажи.Период
    И Цены.Номенклатура = Продажи.Номенклатура
    И Цены.ТипЦен = &ТипЦен
    УПОРЯДОЧИТЬ ПО
    Цены.Период УБЫВ) как Цена
    ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Продажи 

    Показать

    В этом случае вложенный запрос выполнится столько раз сколько записей в основном запросе.

    Reply
  15. Ish_2

    А вообще, пост (14) об относительности совершенства любого алгоритма.

    С точки зрения специалиста 1с — приведенный в (0) алгоритм можно считать образцом.

    Cпециалист же знакомый с SQL воскликнет :

    «Какое извращение ! Из за ограничений языка запросов 1с ребятам приходится кувыркаться и изобретать велосипед с квадратными колесами (0)»

    Reply
  16. I_G_O_R

    (12) насчет среза согласен, но в этом случае я бы делал как в статье, а то мой запрос из красивого и короткого превратится длинный и некрасивый

    Reply
  17. V_V_V

    Народ, ну вам же не надо объяснять, что «длинный» и «некрасивый» запрос 1С 8 — это НЕ всегда неоптимально и малоэффективно… 😀

    Reply
  18. Rabajaba

    Тоже долго думал как собрать среднесписочное количество сотрудников, добавил справочник «ПериодыУчета» с 3-мя полями: «ДатаНачала», «ДатаОкончания», «ВидПериода»(День,Месяц,Год). В результате получить по дням можно все что угодно, при этом запрос остается читабельным и компактным. Ну а для регистров накопления конечно лучше использовать параметр виртуальной таблицы «День».

    Reply
  19. Ish_2

    (12) Так, что , Игорь , поговорили … и всё ?

    В (8) ты привел свой альтернативный вариант запроса. Давай уж выясним до конца :

    Быстрее ли он работает , чем вариант приведенный в статье (0) ?

    На больших объемах данных. Что скажешь ?

    Reply
  20. I_G_O_R

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

    Reply
  21. Ish_2

    (20) Много не пей. Послезавтра ..проверять запрос.

    Reply
  22. I_G_O_R
    Reply
  23. Ish_2

    (22) Знал, что ты ответишь !

    Задал ты задачку.

    Так что же лучше «в среднем» для обычной работы в УТ ?

    Очень похоже , что при отработке твоего запроса при первом запуске sql-сервер создает и грузит в память ВЕСЬ индекс регистра по полю Период. Именно работа по этому индексу съедает 90% времени. При последующих запусках этого не происходит поэтому и время всего 10 сек.

    НО.

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

    Свою первую статью помнишь ? С тех пор прогнозирую с опаской.

    Так что = это всего лишь предположения.

    Если они верны , то тогда я бы отдал предпочтение запросу приведенному в статье ( со всеми необходимыми исправлениями и добавлениями)

    Reply
  24. Ish_2

    (0) Про команду «Индексировать».

    На мой взгляд явно создавать индекс при помощи этой команды нужно только при многократном обращении к таблице . При однократном обращении этого делать не нужно — оптимизатор запроса сам создаст нужный индекс в нужном ему виде.

    Казалось бы это замечание малосущественно. Но.

    Мало того , что явное создание индекса командой «Индексировать» при однократном обащении не дает выигрыша —

    оно еще и даёт очень существенный проигрыш.

    На этот эффект мы натолкнулись с Alex-is при тестировании Примера 3 в его «Заметочках…».

    Reply
  25. I_G_O_R

    (23) а как узнать точно что скл делал всё это время, план выполнения я смотрел и он не меняется

    (24) а насчет индексов: с индексами где-то раза в два медленнее выполнялся запрос

    Reply
  26. Ish_2

    (25) Прав я или нет — можно узнать если на горячем сервере после первого запуска твоего отчета в регистр Цены занести 1000 новых записей. И после этого запустить твой отчет .

    Если время будет 400 сек — то я прав. Если 10 сек — то я не прав.

    Reply
  27. I_G_O_R

    (26) да оказался прав но на 50% )), занес 10000 записей в один день и время запроса составило 175 сек, а потом опять 10. Потом я переписал на временные таблицы свой запрос и добавил в запрос из топика срез последних, в итоге на холодном сервере время выполнения обоих запросов 45-50 сек, на горячем 35-40. Не пойму только почему раньше были более стабильные результаты. Много тестов не делал, той мой запрос выиграет, то из топика, вообщем буду считать что одинаково. В последних запросах теперь мне не нравится, что в регистре Цены номенклатуры куча индексов (в моей базе только по ЦеныНоменклатуры данные — 236 Мб, а индексы 730 Мб), которые почти никак не используются

    Reply
  28. Ish_2

    (27) Сбил с толку. Совсем.

    Смутил этими 50 %(175 сек) , малопонятны пока и (45-50),(35-40).

    Reply
  29. I_G_O_R

    (28)

    малопонятны пока и (45-50),(35-40)

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

    Reply
  30. Ish_2

    (29) Да, нет.

    Малопонятен сам такой эффект . Почему так получилось … такие диапазоны значений.

    Reply
  31. vitaliyua

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

    Reply
  32. ildarovich

    (8) Вот еще короче

    ВЫБРАТЬ
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура,
    СУММА(Продажи.КоличествоОборот) КАК Количество,
    СУММА(Продажи.СтоимостьОборот) КАК Стоимость,
    МИНИМУМ(РАЗНОСТЬДАТ(ЦеныНоменклатуры.Период, Продажи.Период, ДЕНЬ) * &Много + ЦеныНоменклатуры.Цена)
    — МИНИМУМ(РАЗНОСТЬДАТ(ЦеныНоменклатуры.Период, Продажи.Период, ДЕНЬ) * &Много) КАК Цена
    ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ПО Продажи.Номенклатура = ЦеныНоменклатуры.Номенклатура
    И (ЦеныНоменклатуры.ТипЦен = &ТипЦен)
    И (ЦеныНоменклатуры.Период < = Продажи.Период)
    
    СГРУППИРОВАТЬ ПО
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура

    Показать

    Здесь вообще один запрос. Идея в том, чтобы вместо поиска периода, на котором начинается актуальная цена, искать саму цену. Для этого подобрана функция, которая «отправляет» цену прошлого периода на свой «эшелон», высота которого зависит от древности цены. Ширина «эшелона» задается параметром «Много» — это величина, гарантированно перекрывающая диапазон изменения цены (1000000, 10000000 и т.п.). После нахождения ближайшей (нижайшей) «летящей» цены, высота соответствующего «эшелона» вычитается и цена «опускается на землю».

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

    Кроме краткости записи, других достоинств у запроса нет. Минусы — некоторый проигрыш по времени «классическому» запросу из-за большего объема вычислений в группировках, необходимость думать над значением «Много», работа только с простыми типами.

    Reply
  33. Nykyanen

    Аналогичная задача на запросе.

    Тут остатки на каждый день * цены номенклатуры на дату остатком,

    там суммы документов в у.е. * курсы валют на день документа.

    «Курсы валют на разные даты в одном запросе. Делаем свой нестандартный срез последних.»

    http://infostart.ru/public/140933/

    Reply
  34. Flashback1979SE

    ну ваще ниньзя

    PS: тоже люблю максимально работать запросами

    Reply
  35. ManyakRus

    всё правильно, только не забыть:

    1) в первом запросе обязательно сгруппировать данные,

    в несгруппированных все цифры потом задвоятся, затроятся.

    2) в третьем запросе главное не сгруппировывать или обязательно сгруппировывать по периоду

    (я сам не понял почему)

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

    (а может и нет)

    что не нравится:

    группировать по числовым колонкам нежелательно,

    это тоже самое что написать

    ТЗ.Свернуть(«Товар,СуммаРуб»,»СуммаШт») — СуммаРуб должна быть в правой части а не в левой.

    Reply
  36. Поручик

    Спасибо автору, пригодилось для разработки. Почему-то с соединением средствами СКД не взлетело, а чисто на запросе получилось.

    Reply
  37. Поручик

    Пришлось воспользоваться советом из поста (8), иначе при некоторых условиях выдавались не совсем верные цифры

    Reply
  38. Поручик

    (32)

    Делал отчет по перемещениям товаров на основе материалов статьи. Так как в моей базе данных немного, то отчёт работал нормально. Но клиент прислал замечания.

    исходная ситуация № 1

    1) товар используется в документе условно 23 10 2013

    2) цена на начало месяца 1796

    смотрим отчет — всё ок

    исходная ситуация № 2

    1) товар используется в документе условно 23 10 2013

    2) цена на начало месяца 1796

    3) установка цен 20 10 2013 меняет цены продажи на 2000

    смотрим отчет — всё ок

    исходная ситуация № 3

    1) товар используется в документе условно 23 10 2013

    2) цена на начало месяца 1796

    3) установка цен 25 10 2013!!!!!!!!!! меняет цены продажи на 2000 (то есть дата больше документа. Та же проблема если изменить дату документа установки и на ноябрь)

    смотрим отчет: цена = 1796 + 2000 = 3796 (сумма цен на начало месяца и цены, установленной после документа отчета)

    Времени на изучение ситуации и переделку запроса не было, поэтому взял совет из поста 8.

    Reply
  39. Abbra

    Для тех, кто не понимает (пока?) вложенные запросы и соединения есть еще вариант для СКД — Вычисляемые поля.

    Делаем вычисляемое поле Цена и в Выражение ставим — Ценообразование.ПолучитьЦенуНоменклатуры(Субконто1, Субконто2.ТипЦенРозничнойТорговли, Период, Константы.ВалютаРегламентированногоУчета.Получить(), 1, 1)

    Это частный пример для бухгалтерии 2.0.

    И очень надеемся на кеширование. )

    Reply
  40. Gureev

    Столкнулся с проблемой в СКД.

    Ни в какую не хочет показывать данные на дату. Показывает только срез на текущую дату.

    Как только не вертел, не пойму в чем причина.

    Такое ощущение, что из источника в приемник параметр не передается.

    Reply
  41. Gureev

    Решил проблему.

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

    Так заработало… колдунство какое-то.

    Reply
  42. puzakov

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

    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
    {(&Дата)},
    {(Номенклатура = &Номенклатура
    И ТипЦен = &ТипЦен)})

    Иначе отработает неправильно.

    Reply
  43. puzakov

    (3) не согласен. В данном конкретном случае пакетный запрос проигрывает по производительности СКД.

    Reply
  44. ildarovich

    (43) Не может такого быть…

    Reply
  45. puzakov

    (44) почему? В данном случае создаются две временные таблицы, а это не самая производительная операция. СКД же обойдется без записи данных на диск.

    Reply
  46. ildarovich

    (45) Мои умозаключения покажутся Вам такими же умозрительными, как и мне сейчас кажутся Ваши. Просто проведите эксперимент. Он, скорее всего, будет в мою пользу и Вам легче будет поверить объяснениям.

    Reply
  47. puzakov

    (46) по поводу производительности я уже экспериментировал, и не однократно. Может когда-то там, в далекие времена 8.1, СКД и была тормозной, сейчас это не так.

    Reply
  48. ildarovich

    Тогда поподробнее расскажите как тестировали, может быть запрос не оптимально написали, на каких данных тестировали, файловый или серверный вариант, схему компоновки и запрос приведите. Действительно интересно. Просто я сравнивал как-то разные варианты «нарезки» последних. Выяснил, что классический метод из двух запросов очень эффективен — там практически нет потерь времени. Поэтому не представляю, где может быть выигрыш. А то, что Вы говорите в памяти или на диске может не быть решающим фактором.

    Reply
  49. puzakov

    (48) ладно, наваяю.

    Reply
  50. ЧИА

    (48) ildarovich,

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

    если в СКД отборы, то фильтр дает ускорение

    но — это не совсем корректное сравнение — запрос без отбора и СКД с отбором

    если в СКД есть отборы, то в текст запроса надо их тоже добавить, программно, например «из остатки(,»+отбор+»)»

    и тогда сравнивать

    Reply
  51. ЧИА

    _also,

    В том виде, что есть сейчас, если опубликованный запрос запустить на УТ 10 и т.п., неподготовленный человек сильно удивится )

    Во все три выборки надо вставить характеристику

    Reply
  52. _also

    (51) ЧИА, насколько помню, демо-пример вообще на демо УПП писал. Да и учет по характеристикам может быть включен или не включен.

    Reply
  53. Al777

    Статья мне сильно помогла при разработке отчёта на СКД, но с более широкими возможностями. Сделал по второму варианту, но так как выводились не все товары с несуществующими ценами, то сделал связь по типу первого варианта. Чисто первый вариант работает очень долго по всей номенклатуре, поэтому от него отказался, хотя он и проще и быстрее в реализации.

    Reply
  54. natarezn

    (36) Поручик, что делать, если дата пустая ?

    Reply
  55. natarezn

    спасибо за запросы — пригодились!

    Reply
  56. FlyVodolaz

    Запрос должен быть совсем другим, не нужно группировать лишнюю информацию и по максимум сократить подзапрос втМаксПериод

    ВЫБРАТЬ
    ПродажиОбороты.Период КАК Дата,
    ПродажиОбороты.Контрагент КАК Контрагент,
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.КоличествоОборот КАК Количество,
    ПродажиОбороты.СтоимостьОборот КАК Стоимость
    ПОМЕСТИТЬ втБезЦены
    ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК ПродажиОбороты
    
    
    ;
    
    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    втБезЦены.Номенклатура,
    втБезЦены.Дата КАК Дата,
    МАКСИМУМ(ЦеныНоменклатуры.Период) КАК Период
    ПОМЕСТИТЬ втМаксПериод
    ИЗ
    втБезЦены КАК втБезЦены
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ПО втБезЦены.Номенклатура = ЦеныНоменклатуры.Номенклатура
    И ЦеныНоменклатуры.ТипЦен = &ТипЦен
    И втБезЦены.Дата >= ЦеныНоменклатуры.Период
    
    СГРУППИРОВАТЬ ПО
    втБезЦены.Дата,
    втБезЦены.Номенклатура
    ;
    
    ВЫБРАТЬ
    втМаксПериод.Номенклатура,
    втМаксПериод.Дата КАК Дата,
    ЦеныНоменклатуры.Цена КАК Цена
    ПОМЕСТИТЬ втЦеныНаДату
    ИЗ
    втМаксПериодКАК втМаксПериод
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ПО втМаксПериод.Номенклатура = ЦеныНоменклатуры.Номенклатура
    И втМаксПериод.Период = ЦеныНоменклатуры.Период
    И ЦеныНоменклатуры.ТипЦен = &ТипЦен
    
    ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    Дата
    
    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    втБезЦены.Дата,
    втБезЦены.Контрагент,
    втБезЦены.Номенклатура,
    втБезЦены.Количество,
    втБезЦены.Стоимость,
    втЦеныНаДату.Цена
    ИЗ
    втБезЦены КАК втБезЦены
    ЛЕВОЕ СОЕДИНЕНИЕ втЦеныНаДату КАК втЦеныНаДату
    ПО втБезЦены.Номенклатура = втЦеныНаДату.Номенклатура
    И втБезЦены.Дата = втЦеныНаДату.Дата
    
    АВТОУПОРЯДОЧИВАНИЕ
    

    Показать

    Reply
  57. WellMaster

    (22) I_G_O_R,

    Дополню.

    Если подобная конструкция используется в Универсальном отчете (или вообще через ПостроительОтчетов), то может оказаться, что пользователь не задал «Период» как одно из измерений строк. При этом отчет будет ругаться на строчку

    Цены.Период <= Продажи.Период

    (не будет находить Поле «Период» таблицы «Продажи»).

    Решением проблемы может стать способ

    Цены.Период <= Продажи.ДокументПродажи.Дата

    З.Ы. Проверено на себе

    Reply
  58. puzakov

    (57) WellMaster,

    Цены.Период <= Продажи.ДокументПродажи.Дата

    слишком злобная конструкция. Задачу-то она может и позволит решить, да только в большой базе может сильно ударить по производительности…

    Reply
  59. WellMaster

    (58) puzakov, не спорю. Но хоть что-то.

    Reply
  60. ui69

    А как вычислить стоимость Цена*Количество, если Количество в первом наборе данных, а Цена во втором?

    Reply
  61. KNOSSOS

    (8) I_G_O_R,

    ВЫБРАТЬ
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура,
    Продажи.КоличествоОборот КАК Количество,
    Продажи.СтоимостьОборот КАК Стоимость,
    ЦеныНоменклатуры.Цена
    ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ПО Продажи.Номенклатура = ЦеныНоменклатуры.Номенклатура
    И (ЦеныНоменклатуры.ТипЦен = &ТипЦен)
    И (ЦеныНоменклатуры.Период В
    (ВЫБРАТЬ ПЕРВЫЕ 1
    Цены.Период
    ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК Цены
    ГДЕ
    Цены.Период <= Продажи.Период
    И Цены.Номенклатура = Продажи.Номенклатура
    И Цены.ТипЦен = &ТипЦен
    УПОРЯДОЧИТЬ ПО
    Цены.Период УБЫВ))
    

    Показать

    Вышел на эту тему по поисковику. Предложенный запрос очень лаконичен, но сильно просаживает производительность. Причем на файловом варианте 8.3 на порядок медленнее чем на MS SQL2012.

    А вот так будет стрелять везде:

    ВЫБРАТЬ
    Продажи.Период,
    Продажи.Контрагент,
    Продажи.Номенклатура,
    Продажи.КоличествоОборот КАК Количество,
    Продажи.СтоимостьОборот КАК Стоимость,
    ЦеныНоменклатуры.Цена
    ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ПО Продажи.Номенклатура = ЦеныНоменклатуры.Номенклатура
    И (ЦеныНоменклатуры.ТипЦен = &ТипЦен)
    И (ЦеныНоменклатуры.Период В
    (ВЫБРАТЬ МАКСИМУМ(Цены.Период)
    ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК Цены
    ГДЕ
    Цены.Период <= Продажи.Период
    И Цены.Номенклатура = Продажи.Номенклатура
    И Цены.ТипЦен = &ТипЦен
    ))
    

    Показать

    Reply
  62. Поручик

    (61) Вот примерно так я и сделал года два назад в паре своих отчётов.

    Reply
  63. I_G_O_R

    (61) Ха! Тема еще жива))

    Сам тоже по возможности пользуюсь именно вторым вариантом. Но что еще интересно, сами 1С такие запросы до сих пор не юзают, наверное не на всех СУБД такие запросы нормально работают, но я сейчас пишу только под ms sql, там отлично работают.

    Reply
  64. Chandr73

    Спасибо большое, пригодилось!

    Reply
  65. aleksdiez

    Условие по ТипуЦен надо указывать в связи пацаны 😉

    Reply
  66. BAE1234567

    Оч. здорово! Спасибки за статью!

    Reply
  67. vis_tmp

    (10)(22) Спасибо, отлично работает!

    Reply
  68. KazanKokos

    отлично. как раз нужно

    Reply
  69. SmArtist

    Спасибо!

    Reply
  70. inf012

    Спасибо!

    именно по вашей статье получилось

    Reply
  71. rick_77

    Добрый день!

    Вопрос подобный.

    Возможно ли средствами 1с вывести список материалов, которые не списаны более 30 ней?

    Спасибо!

    Reply
  72. user693558_zdorenkomail

    Подскажите как в данный пример прикрутить пересчет валютной цены из регистра ЦеныНоменклатуры по курсу?

    Reply
  73. user1090556

    (63)

    Но что еще интересно, сами 1С такие запросы до сих пор не юзают

    потому что большинство пользуются конструкторами запросов. А условие

    ВЫБРАТЬ МАКСИМУМ(Цены.Период)
    ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК Цены
    ГДЕ
    Цены.Период <= Продажи.Период
    И Цены.Номенклатура = Продажи.Номенклатура
    И Цены.ТипЦен = &ТипЦен)

    В конструкторе будет вызывать ошибку.

    Reply
  74. twilight_dream

    Большое спасибо! Избавили от мук творчества и сэкономили массу времени и нервов.

    Ну, то что тип цен в левое соединение нужно прикручивать, надеюсь, кто хотел — догадался.

    Reply
  75. user1022955

    Автор ты молодец конечно.

    Но твой пример будет работать только если у тебя в базе один-единственный вид цен.

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

    Reply
  76. d4rkmesa

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

    Reply

Leave a Comment

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