Регистры сведений 1С. Как это устроено.

13.01.17

Разработка - Математика и алгоритмы

Основная идея публикации - собрать в одном месте информацию о внутреннем устройстве такой абстрактной сущности, как "Регистр сведений 1С" и ответить на ряд вопросов: Что происходит при записи регистра в различных режимах? Что такое на самом деле "СрезПервых" и "СрезПоследних"? Как оптимально выбрать структуру регистра? Это та информация, владея которой, начинаешь лучше понимать как это работает и как правильно использовать регистры сведений.

Публикация имеет характер исследования, поэтому подробная и объемная. Кто не любит много букв - можно начать с выводов, они специально выделены в содержании.

Для тех, кто только делает первые шаги в 1С: не смотря на подробность изложенной информации и её справочно-обучающую интонацию, тут нет самых азов. Подразумевается, что читатель уже имеет базовое понимание и практику конфигурирования в 1С. Статья не учит как программировать, но она помогает найти ответы на вопросы "А как правильно и почему именно так?".

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


Содержание:

1. Архитектура регистра на уровне СУБД.

- индексы непериодического, независимого регистра сведений

- индексы непериодического, подчиненного РС

- индексы периодического, независимого РС

- индексы периодического, подчиненного РС

- индексы периодического РС с периодичностью "По позиции регистратора"

- индексы таблицы итогов среза первых и среза последних

Выводы.

2. Разбираемся с записью регистров

Независимые регистры сведений.

- запись "по умолчанию"

- запись без замещения

- запись без проверок

Менеджер записи

Подчиненные регистры сведений.

- запись "по умолчанию"

-

DatabaseCompressionTool — сжатие и свертка любой базы 1С

Инструмент DatabaseCompressionTool (DCT) позволяет безопасно сжать и свернуть любую базу 1С, освободив сотни гигабайт и увеличив производительность системы. Доступна демо-версия для оценки эффективности.


Вступайте в нашу телеграмм-группу Инфостарт

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Математика и алгоритмы Программист 1С 8.3 Абонемент ($m)

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

1 стартмани

07.11.2025    6205    16    InFlach    17    

27

Математика и алгоритмы Запросы Программист 1С:Предприятие 8 Бесплатно (free)

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

08.07.2024    8055    ivanov660    9    

24

Математика и алгоритмы Программист 1С:Предприятие 8 1C:Бухгалтерия Россия Абонемент ($m)

На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL. Я же покажу как можно сократить объем данных в 49.9 раз при этом: 1. Сохранить значения локальных экстремумов 2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.

1 стартмани

30.01.2024    15809    stopa85    12    

43

Математика и алгоритмы Бесплатно (free)

Разработка алгоритма, построенного на модели симплекс-метода, для нахождения оптимального раскроя.

19.10.2023    24038    user1959478    57    

41

Математика и алгоритмы Разное 1С:Предприятие 8 1C:Бухгалтерия Россия Абонемент ($m)

Расширение (+ обработка) представляют собою математический тренажер. Ваш ребенок сможет проверить свои знание на математические вычисление до 100.

2 стартмани

29.09.2023    14778    maksa2005    8    

27

Математика и алгоритмы Инструментарий разработчика Программист 1С:Предприятие 8 Россия Абонемент ($m)

Что ж... лучше поздно, чем никогда. Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.

1 стартмани

09.06.2023    24136    11    SpaceOfMyHead    20    

65

Математика и алгоритмы Программист 1С:Предприятие 8 Бесплатно (free)

Три задачи - три идеи - три решения. Мало кода, много смысла. Мини-статья.

03.04.2023    16702    RustIG    10    

30

Механизмы платформы 1С Математика и алгоритмы Программист 1С:Предприятие 8 Россия Бесплатно (free)

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

23.11.2022    15654    gzharkoj    17    

27
Вознаграждение за ответ
Показать полностью
Отзывы
51. MaxMNSH 30.08.16 17:46 Сейчас в теме
(40) speshuric,

Предлагаю на обсуждение следующий концепт(идею) решения:

Первый регистр сведений "Регистр1" простой периодический для истории

Период
Заявка
Статус

Дополнительный регистр сведений "Регистр2" не периодический помимо обычного с историей

Измерения:
ДатаЗаписи
Статус
Заявка

Ресурс:
ДатаУстановкиСтатуса


Константа в которой настроим конечный статус заявок. В нашем случае в ней статус "Сделано"

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

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

РегламентноеЗадание работает и выбирает зарегистрированые заявки:

Если заявка в текeщем статусе более N Дней, то пишем в регистр2 ее еще раз с ДатаЗаписи = ТекДата, ДатаУстановкиСтатуса = Та
дата когда статус реально был установлен, и сам статус = тек статус
(N дней = Настройка общая для робота, можно хранить в другом периодическом регистре что бы была возможность гибко поменять
настройку и знать какое количество дней было на любую дату)

Т.е. в течении всего времени пока заявка в одном и том же статус она каждые N дней отписывается в регистр2 (добавляется новая
запись ДатаЗаписи = ТекДата на момент записи) о том что статус сохранился.

Итого получаем, что в запросах на анализ переходов статусов Новый – ВРаботе (в общем виде м-ду любыми двумя статусами кроме конечного), а так же в запросе всех заявок в статусах новый/Вработе на произвольную дату

у нас будет ВСЕГДА ограничение первичной выборки по


ИЗ Регистр2 КАК Регистр2

ГДЕ
ДатаЗаписи МЕЖДУ &НачПериода И &КонецПериода (выборка всегда конечна и ее размер не сильно зависит от размера всей таблицы)

+при необходимости условие на &статус

Важно: отбор по периоду в первичной выборке надо увеличивать от дат заданных юзером что бы он был более шага N отписки в регистр повторных записей
А далее фильтровать полученные временные таблицы уже по периоду который задал юзер в отчете &ДатаНач и &ДатаКон по ДатаУстановкиСтатуса

Таким образом мы получим все заявки на нужные даты(или интервал дат) по нужным статусам и можем посчитать как среднее время перехода м-ду статусам так и вывести сами заявки в статусах

Итого получаем

1. Запросы для пунтков 1 и 2 "Какие данные хотим получать" ВСЕГДА будут ограничены условием МЕЖДУ по ДатаЗаписи и
зависимость от общего объема данных будет много меньше линейной (всегда берем только отрезок по датам)

2. пункт 3 "Какие данные хотим получать" получаем из основного периодического регистра ("наивной структуры") историю
ВСЕГДА с отбором по конкретной заявке/списку


3. На статусы мы не завязаны и не хардкодим их программно, пользователи могут добавлять и удалять их, а так же можно сделать
настраиваемым список статусов которые хотят проанализировать пользователи в отчетах п.1 и п.2 "Какие данные хотим получать"

Ограничение: должен быть "конечный" статус заданный в константе. (в примере это статус "сделано").
И данные по нему (какие заявки есть на произвольную дату в этом статусе) мы можем получить корректно только срезом по обычному регистру
истории, но в условии задачи у нас и не указан этот статус для оперативного(быстрого) анализа.

Минусы: Будет избыточность записей в Регистр2 (больше записей чем в регистре истории) но зависимость роста его, при
нормальном подборе N дней, будет меньше чем n*ln(n).

Настраивается параметром "N" дней насколько часто бы будем отмечать заявку в Регистр2. Чем больше N тем
меньше будет увеличение объема от общего числа заявок, Но тем больше будет первичная выборка которая должна захватить
интервал N. Выбирается оптимальный вариант соотношения ОбъемДанных/Количество строк первичной выборки, исходя из статистики
перехода статусов по заявкам и требований к времени выполнения запроса. Например берем 10 дней, 4 дня и т.п.

Настройки можно менять при необходимости по ходу жизни системы в связи с добавлением статусов или при изменении статистики
перехода статусов и нагрузки на базу.
jagon; kaaasteeen; user645801_yyyuuu123q; alk; V4L; Ruden95; Sergey.Noskov; speshuric; +8 Ответить
Остальные комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. necropunk 11 05.08.16 16:37 Сейчас в теме
Фундаментальненько. Особенно порадовала картинка, как раз ХЗ сейчас слушаю. Сейчас почитаем...
kaaasteeen; user604633_slevin64celevra; +2 Ответить
2. ruizave 05.08.16 17:29 Сейчас в теме
Очень полезно. Спасибо. Оказывается грешил постоянными срезами последних с указанием даты. Кажется пора переписывать запросы ))
5. Sergey.Noskov 1433 06.08.16 15:32 Сейчас в теме
(2) ruizave, если в запрос передается "текущая дата", то да, пора переписывать)) Регистров накопления это, кстати, тоже касается...
166. sss999 50 08.09.22 23:25 Сейчас в теме
(2) а что не так с текущей датой?
3. Evil Beaver 8412 05.08.16 20:09 Сейчас в теме
Видишь пост автора - плюсуй, не читая!
kaaasteeen; ulfext; troubleshooter; artbear; antoha.o; +5 Ответить
6. Sergey.Noskov 1433 06.08.16 15:36 Сейчас в теме
(3) Evil Beaver, я рассчитываю, что читать все-таки будут ;)
iulyus; V4L; +2 Ответить
22. Evil Beaver 8412 08.08.16 18:54 Сейчас в теме
(6) ну и читать обязательно, конечно ))
4. Solovyeff 05.08.16 23:26 Сейчас в теме
Очень хорошая статья, в закладки однозначно. Надеюсь, что автор не закончит на регистрах сведений и будет продолжать нас радовать подобными статьями и далее.
wau8824ru; kaaasteeen; user867197; Емельянов Алексей; AlbinaAAA; Гриффин; DarkUser; max996; Makushimo; artbear; PrinzOfMunchen; user597751_a.yasmenko; McCoy77; NeviD; Berckk; Гобсек; Andry.Boris; doom_2001; antoha.o; CyberCerber; 1cWin; Irwin; tormozit; andreyparmuzin; fancy; bulpi; mi1man; Йожкин Кот; +28 Ответить
7. RailMen 835 06.08.16 18:26 Сейчас в теме
Статья - алмаз!
kaaasteeen; AlbinaAAA; antoha.o; +3 Ответить
8. Synoecium 793 06.08.16 20:27 Сейчас в теме
Люблю такие фундаментальные статьи, когда все по одной теме собрано вместе и просится в закладки)
9. coolseo 80 07.08.16 03:32 Сейчас в теме
Темная лошадка - раскрыта!
Огромное спасибо. В закладки
10. Yashazz 4933 07.08.16 12:56 Сейчас в теме
Капитально. Многое, конечно, уже опубликовано в сети и есть в "Проф.разработке" или у Филиппова, но в целом - внушает. Спасибо)
11. tormozit 7384 07.08.16 19:45 Сейчас в теме
Статья отличная!

Таблицы итогов регистров сведений в платформе можно было бы сделать по аналогии с регистрами накопления - Таблица текущих итогов (фактически она сейчас и реализована в платформе) и таблица ежемесячных итогов (срез последних/первых на каждый месяц). При выполнении запроса СрезПоследних(&Дата) опять же аналогично регистрам накопления можно было сделать дорасчет среза от ближайшего ежемесячного итога. Понятное дело, что это - в разы сложнее и объемнее доработка, но считаю что СрезПоследних(&Дата) достаточно часто встречается в прикладных решениях, чтобы эти затраты были оправданы. Самое сложное будет пересчитывать ежемесячные итоги при записи, но опять же большая часть логики будет реализована по аналогии с уже отлаженной для регистров накопления.
12. mkalimulin 1643 07.08.16 20:11 Сейчас в теме
(11) tormozit, Лучше РН сделать, как сделали РС. Т.е. хранить только актуальные итоги.
13. genayo 08.08.16 07:50 Сейчас в теме
(12) Так а сейчас что мешает оставить в РН только текущие итоги?
14. mkalimulin 1643 08.08.16 10:00 Сейчас в теме
(13) genayo, Я что-то пропустил? В РН можно управлять итогами остатков?
16. genayo 08.08.16 10:26 Сейчас в теме
(14) В 8.2 для остаточных регистров можно оставить только текущие итоги, правда не совсем штатными средствами, отредактировав служебные таблицы регистров в СУБД напрямую. Проверено, работает...
17. mkalimulin 1643 08.08.16 10:33 Сейчас в теме
(16) genayo, Редактировать таблицы напрямую после каждого проведения?
20. genayo 08.08.16 11:56 Сейчас в теме
(17) Нет, в служебную таблицу регистра _AccumRgOpt в поле _Period записать дату из далекого прошлого, в поле _UseTotals Ложь, в поле _ActualPeriod 1
31. Steelvan 317 13.08.16 06:48 Сейчас в теме
Таких горе-спецов как (20) гнать с работы за такое.
Своими корявыми ручонками лезут в таблички, а потом делают круглые глазки, типа это оно само, когда отчеты врать начинают.
Или когда ошибки после перепроведения появляются.
Насмотрелись на таких.
161. sss999 50 08.09.22 23:09 Сейчас в теме
(31) человек явно знает что делает, но таким мазохизмом чтобы заниматься нужен склад ума конечно, ну и это явно старые технологии, когда ссд не стоили копейки как щас
18. gubanoff 63 08.08.16 10:57 Сейчас в теме
(14) Можно, нужно просто не использовать итоги и не пересчитывать их - и их не будет, останутся только текущие итоги.
УстановитьИспользованиеИтогов(<Признак>)
Параметры:

<Признак> (обязательный)

Тип: Булево.
Признак использования итогов.
Описание:

Устанавливает признак использования итогов. Если использование итогов отключено, то при записи набора записей регистра не будет производиться пересчет итогов, но при этом будут не доступны виртуальные таблицы расчета остатков и оборотов.
19. mkalimulin 1643 08.08.16 11:14 Сейчас в теме
21. Sergey.Noskov 1433 08.08.16 13:24 Сейчас в теме
(11) tormozit, (12) mkalimulin, мое мнение (из опыта), какие итоги вести должно настраиваться разработчиком - задачи разные бывают. И, в идеале, таблиц итогов должно быть две - "ежемесячные" итоги и текущие итоги. Так проще будет выполнять регламентные операции (таблица "простых" итогов будет реже фрагментироваться).

Оставить в РН только текущие итоги достаточно просто, надо установить период рассчитанных итогов на дату, раньше которой в регистре нет движений, после этого пересчитать итоги.

>но опять же большая часть логики будет реализована по аналогии с уже отлаженной для регистров накопления
тут можно поспорить, т.к. для РН все проще - у них ресурсы только цифровые и в итогах нет реквизитов. Следовательно и "апдейтить" итоги проще - добавить или вычесть новое значение ресурса. В РС придется удалять все рассчитанные итоги и создавать заново и чем больше месяцев надо хранить срезы, тем больше будет накладных расходов. Да и профит от дорасчета (а это плюс UNION) будет далеко не всегда, при текущей структуре индексов в 8.3, СУБД достаточно эффективно рассчитывает "срез", если обратили внимание на скрины планов запросов - таблица регистра читается один раз, хотя в запросе есть JOIN.
24. asved.ru 37 09.08.16 22:49 Сейчас в теме
(11) tormozit, бессмысленно. При расчете среза (т.е. результата на конкретный момент) регистра накопления имеют значение все его предыдущие записи. Для регистра сведений - только единственная запись - самая старшая предыдущая.
26. tormozit 7384 09.08.16 23:07 Сейчас в теме
(24) Не обязательно хранить в таблицах итогов регистров сведений значения ресурсов и реквизитов. Достаточно хранить только значение последнего/первого периода по каждому набору измерений.
15. Новиков 292 08.08.16 10:26 Сейчас в теме
Сколько ж автор потратил времени на сей труд?
23. asved.ru 37 09.08.16 22:43 Сейчас в теме
Условие виртуальной таблицы тоже не должно быть сложным, например, соединение виртуальной таблицы без условиия с временной с целью выбрать только записи, существующие во временной таблице, будет в большинстве случаев эффективнее, чем условие виртуальной таблицы ххх В (Выбрать из ВременнаяТаблица). MSSQL 2012 такие нюансы точно сам не понимает, а на 2014 не проверял - там другие проблемы с новым cardinarity estimation.
Планы показывать лень, поверьте на слово.

кстати о планах: фактические планы будут выглядеть на иллюстрациях гораздо нагляднее. А текстовые - еще нагляднее, только их мало кто читать умеет :)
25. speshuric 1343 09.08.16 23:04 Сейчас в теме
Добротно.
Пара нюансов:
  • Если уж говорим про ОбменДанными, давайте говорить и про то, как регистрируется в планах обмена. В некоторых случаях это дороже, чем сама вставка в РС.
  • Если не смотрели, то посмотрите, как себя ведет РС с большим количеством (да-да-да, моих любимых) составных измерений. Лучше что-нибудь типа "и ссылка и не ссылка". Посмотрите - волосы на жё... жёлтой книге зашевелятся.
  • Если уж говорите, что "срез последних превращается в сложный запрос", и рекомендуете использовать именно условия в виртуальной таблице, то могу привести контрпримеры. Простые условия на "=" нужно кидать в ВТ. Сложные с В (ВЫБРАТЬ ) или повторяющие другие фильтры запроса - сначала подумать и проверить что там нагенерит 1С. Я, конечно, про MS SQL.
  • Ну и да, самая острая бритва и самая опасная ошибка - программисты запихивают в фильтр ВТ ограничения по реквизитам или ресурсам. Такую ошибку в мало-мальски крупной системе (с горкой отчетов-запросов) не найти пока она не выстрелит.

Кстати, моё мнение: Регистры сведений - одна из самых костыльных структур в 1С, как это не странно. Если интересно могу обосновать с примерами.
user1067759; alk; +2 Ответить
27. Sergey.Noskov 1433 10.08.16 08:18 Сейчас в теме
(25) speshuric
Кстати, моё мнение: Регистры сведений - одна из самых костыльных структур в 1С, как это не странно. Если интересно могу обосновать с примерами.

Конечно интересно!
29. Sergey.Noskov 1433 10.08.16 12:12 Сейчас в теме
(25) speshuric,
Если уж говорим про ОбменДанными, давайте говорить и про то, как регистрируется в планах обмена.

Возможно и стоит поговорить, но это функционал, напрямую не связанный с регистрами.

Если не смотрели, то посмотрите, как себя ведет РС с большим количеством (да-да-да, моих любимых) составных измерений.

С составными типами хорошо знаком, но, опять таки, это отдельная большая тема и понимание принципа работы с составными типами должно быть общим (не побоюсь этого слова - глобальным), хотя зародить зерно знаний на каком то примере можно, подумаю.
Кстати, в 8.3.8 структура индексов для составных типов сильно изменилась (V8Update), теперь все поля входят в один индекс. А уже в 8.3.6 (может и раньше, не замечал), фильтр вида <<...ГДЕ Реквизит_ВсеПримитивы_ЛюбаяСсылка = 10 >> преобразуется в запросе к такому условию:
WHERE 
   (T1._Fld361_TYPE = 0x03 AND 
   T1._Fld361_L = 0x00 AND 
   T1._Fld361_N = @P1 AND 
   T1._Fld361_T = @P2 AND 
   T1._Fld361_S = @P3 AND 
   T1._Fld361_RTRef = 0x00000000 AND 
   T1._Fld361_RRRef = @P4)
Показать

т.ч. платформа постепенно вырастает из детских болячек (ну и есть повод обновить статью ;) )

...Сложные [условия в запросе] с В (ВЫБРАТЬ ) или повторяющие другие фильтры запроса - сначала подумать и проверить что там нагенерит 1С. Я, конечно, про MS SQL.

Согласен. Сначала подумать, проверить план запроса и подумать еще раз))

Ну и да, самая острая бритва и самая опасная ошибка - программисты запихивают в фильтр ВТ ограничения по реквизитам или ресурсам.

Точно, забыл! Спасибо, отличная тема для дополнения статьи.
36. speshuric 1343 23.08.16 00:03 Сейчас в теме
(29)(30)
Извиняюсь, что медленно отвечаю.
(29)
В составных полях плохо было не только, что много кривых индексов создавалось, но и то, что при потенциальном размере индекса больше 16 полей он не будет уникальным. Он будет создан, но неуникальным. Как при этом будет работать "ОбменДанными.Загрузка = Истина" я не знаю (подозреваю, что будет грузить всё, что подсунут).

В архитектуре есть проблемы. Во-первых слишком много разных (принципиально разных) назначений у этого вида метаданных. Эти назначения конфликтуют в том смысле, что требуют разной реализации на нижнем уровне. Во-вторых, если говорить только о периодических РС, то главная беда - нельзя эффективно с точки зрения СУБД и при этом правильно с точки зрения платформы "прекратить" вхождение в срез. Третья проблема в том, что сам "срез" - это костыль (ниже чуть подробнее).

И, да, несмотря на это я считаю, что именно РС были самым большим прорывом 1С8.

Смотрите, наши РС, кроме "специальных" использований (типа адресации БП и подобных) нужны для примерно следующих задач:
  • "Замена" периодических реквизитов.
  • Цены номенклатуры и подобные структуры (котировки, ставки налогов, тарифы и т.п.)
  • История статусов чего-бы то ни было (сотрудников, заявок)
  • Связь многие ко многим (непериодические)
  • Тупотаблица (логи, загруженные внешние данные, разные структуры для оптимизации)
  • "Вынесенные" реквизиты объектов (например, непериодический регистр с большим набором ресурсов) - это может понадобиться для деления по блокам или правам или что-то подобное. Сюда же при всех отлиичиях в структуре можно отнести хранилища файлов и картинок.
  • EAV или подобные "универсальные" фиговины. Я их не люблю, но это не значит, что их нет.
Есть и другие, но, пожалуй, что 90-99% описываются именно этим.

С непериодическими регистрами всё просто и почти понятно. Непонятно только зачем их смешали с периодическими, ну да ладно. Про периодические, что с ними не так?
1. Сам срез. У нас уже все СУБД в свежих версиях: SQL Server, Oracle, Postgres, и, кажется, даже DB2 - бодро освоили LAST_VALUE() из SQL2003, как впрочем, и другие оконные функции. 1С не использует LAST_VALUE(), хотя на больших запросах это могло бы дать прирост. Это, скорее всего без существенной доработки SDBL не реализовать. С другой стороны, если оконные функции запихнуть в SDBL и отдать в язык запросов, то на кой леший нам вообще срезы, если они без предрасчитанных итогов?
2. В кейсе "история цен" почти всегда возникает проблема, когда старые разрезы накапливаются. У одежды и обуви - каждый год новая коллекция. В котировках каждые три месяца новые фьючерсы и опционы. В электронике торговать ерундой двухлетней давности - тоже скорее исключение. Как "красиво" их исключить? Завести логический ресурс "я кончился"? По нему будет не очень эффективный отбор в срезе. Завести отдельный непериодический РС? Запаришься целостность обеспечивать. Сделать синтетический РН (сбрасывать остаток в 0)? Геморрно, хотя и работает, если пересчитывать, но ведь это явный костыль.
3. Еще хуже ситуация в задаче "статусы". Предположим У нас у заявки есть 3-5 статусов. 1С-ник не задумываясь создаёт РС "СтатусыЗаявок", измерение Заявка, ресурс Статус (даже в общем-то всё равно что за ссылки в заявках и статусах). Заявки обрабатываются за 1-2 дня. Мы хотим получать ответ на вопросы: а) "какие заявки были не в конечном статусе на дату ДД.ММ.ГГГГ" (Всё просто подумал программист и написал СрезПоследних) и "Сколько заняла обработка заявок из статуса ЧешемЖёлуди до статуса ВаляемКоня" (Тут программиист задумался и начал строчить нечитаемые запросы с джойном нескольких срезов. Ну по крайней мере так делает большинство 1Сников на этой задаче, хотя это и неправильно). В чем же проблема? А проблема в том, что наши статусы неселективны. Ужасно неселективны. При линейном прохождении их будет по 1/5-1/3 в нашем условии. А фильтр только уже в срезе и не работает (итоги и танцы с бубном чуть-чуть облегчат учесть, но лишь чуть-чуть).А теперь выясняем, что у нас заявок от 100000 в день и есть вероятность, что понадобится еще пара статусов. И эти два вопроса из ТЗ тоже задаются часто. Да, есть тот же трюк с РН и итогами, но это и в этом случае странный костыль (и последовательности документов).

Как эти задачи решить в SQL - я могу объяснить. Решения достаточно шаблонные, но впрямую их транслировать в РС 1С не получится (получится, но они будут сильно усложненные и шаблон "замылится"). На уровне платформы эта задача сейчас не решена.
kaaasteeen; SagittariusA; user1021675; Dementor; alk; vandalsvq; artbear; JohnyDeath; tormozit; ToTMoM; aexeel; NeviD; mba1979; Sergey.Noskov; ildarovich; +15 Ответить
37. Sergey.Noskov 1433 23.08.16 13:45 Сейчас в теме
(36) speshuric,
Как при этом будет работать "ОбменДанными.Загрузка = Истина" я не знаю (подозреваю, что будет грузить всё, что подсунут)

В статье есть ответ, при использовании Записать(Истина) всегда выполняется удаление "старых" строк. Проблемы будут только для независимых РС в режиме "заливки" строк, т.е. ОбменДанными.Загрузка = Истина и Записать(Ложь), но тут и режим специфичный, его бездумно применять нельзя.

Касаемо описанных кейсов использования РС, однозначно согласен, что возможностей "взрослых" СУБД в платформе не хватает.
Но сценарий 3 включают в себя несколько прикладных задач и решать их следовало бы отдельно, а суть проблемы как раз в том, что "1С-ник не задумываясь создаёт РС"(с) т.к. РС описанной архитектуры имеет смысл для задач "Какой текущий статус заказа N ?" и "Какой статус был у заказа N на дд.мм.гггг?". Задачи "какие заявки были не в конечном статусе на дату ДД.ММ.ГГГГ" и "Сколько заняла обработка заявок из статуса ЧешемЖёлуди до статуса ВаляемКоня" надо решать отдельно и да, скорее всего регистрами накопления.
Я не верю в универсальное назначение таблиц, точнее сказать не верю в их эффективность и оптимальность. Да, есть задачи, когда можно удачно заюзать один объект под разную логику, но чаще всего это выливается в экономию места на диске ценой бо'льших затрат CPU и RAM.
JohnyDeath; speshuric; +2 Ответить
38. speshuric 1343 24.08.16 00:24 Сейчас в теме
(37)
Конечно, я имел в виду и с Записать(Ложь). Засада в том, что пока индекс уникален хотя бы СУБД бдит за целостность. И потенциально разное поведение на разных СУБД. В общем меня эта штука напрягает.

LAST_VALUE - это не "возможности взрослых СУБД", а то, что включили в стандарт SQL2003. 13 уже лет как. Так что пора бы и в 1С начать запихивать (так и слышу Ага, вот прямо завтра, как с утра, зубы почистим, EXISTS в язык запросов добавим, баги починим и сразу же займемся)
На курсах, в книгах, в типовых статусы прямо учат делать на РС. Так что наш "средний 1Сник" неуиновен. РН тоже противное решение. Вот если интересно - реально попробуй сделать на 1С хорошее решение по этому кейсу на 10М заявок в год и 3 статуса (новый, в работе, закрыт). Каждая заявка закрывается случайно за 1-10 часов.

В любом случае спасибо за статью - не так часто уже что-то новое про 1С узнаётся, а тут целая революция в индексах.
39. Sergey.Noskov 1433 25.08.16 11:47 Сейчас в теме
(38) speshuric,
попробуй сделать на 1С хорошее решение по этому кейсу на 10М заявок в год и 3 статуса (новый, в работе, закрыт). Каждая заявка закрывается случайно за 1-10 часов.

Интересно, каковы у тебя критерии "хорошего решения"? У нас реализована задача похожая на "Сколько заняла обработка заявок из статуса ЧешемЖёлуди до статуса ВаляемКоня" для документа количеством почти 20М в год и числом статусов 10-15. Сделано на РС, но то самое "время обработки" вычисляется сразу.
40. speshuric 1343 26.08.16 01:44 Сейчас в теме
(39)
Если всерьёз, то давай сформулирую точно.

Существующие объекты:
1. Справочник "Заявки". Почему справочник, а не документ? Чтобы не было соблазна делать его регистратором. На самом деле для решения это не важно. Некий ссылочный тип. Добавлять реквизиты в этот справочник нельзя.
2. Перечисление "Статусы": "Новый", "ВРаботе", "Сделано". Решение должно быть написано так, чтобы оно не зависело от количества статусов. Добавление статуса не должно ломать решение.

Входные данные
Заявки примерно каждые 10 секунд начинают путь в статусе "Новый". Потом они переходят в статус "ВРаботе", при этом:
  • 20% заявок переходят примерно через 5 минут
  • 20% заявок переходят примерно через 30 минут
  • 20% заявок переходят примерно через 2 часа
  • 20% заявок переходят примерно через 8 часов
  • 10% заявок переходят примерно через 3 суток
  • 9% заявок переходят примерно через 15 суток
  • 1% заявок переходят примерно через 30 суток, но могут быть единичные заявки висящие существенно дольше.
Из статуса "ВРаботе" заявки переходят в статус "Сделано". Распределение по времени аналогичное, но в 3 раза дольше. При этом не гарантируется каких либо зависимостей между временем первого этапа и второго. Т.е. быстро взятая в работу заявка не обязательно быстро сделается и наоборот. Если будет принципиально, то набор данных я сгенерирую, но мне кажется, что всё понятно.

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

Какие данные хотим получать
1. Вывести все заявки в статусах "Новый", "ВРаботе" на момент времени T0. T0 может быть как в начале истории всех заявок, так и в середине и в конце.
2. За период ДатаНач-ДатаКон вывести среднее время по заявкам, проведенное между статусом "Новый" и "ВРаботе". Есть важные уточнения:
2.1. Заявки попадают в выборку, если в течение этого периода они были в статусе Новый. В том числе, если они были созданы ранее, чем ДатаНач.
2.2. По всем заявкам (как созданым раньше, чем ДатаНач, так и позже) время ожидания считается с момента, когда заявка стала в статусе "Новый". По заявкам, которые попали в выборку, но не были переведены в статус "ВРаботе" вместо даты перевода в этот статус берется ДатаКон. Ожидается, что период задаётся обычно по 5 дней, но может и больше.
3. Очевидно, что должна быть и полная история статусов всех заявок (за период). Это автоматом есть в РС "наивной структуры" в виде формы списка и если структура стала сложнее, то нужно не потерять простые штуки.

Решение не должно хардкодить статусы. То есть воркфлоу может измениться!

Нефункциональные требования
  • Ожидается, что система сможет работать без "свертки" 3 года или более.
  • Ожидается, что механизм записи истории статусов может быть достаточно параллельным. То есть блокировать любую запись в систему на время записи одного элемента - плохо. Если блокируется только одна или небольшое количество заявок, то это нормально. Гарантии, что более ранние
  • Время записи нового элемента истории зависит от размера истории не больше чем O(ln n), где n-размер истории. Если не получается, то хотя бы не более чем O(sqrt (n)). И ожидается что в абсолютных значениях запись занимает меньше 1 секунды.
  • Ожидается что получение данных из пп 1-3 занимает не более O(ln n + m), где n-размер истории, а m - количество событий попавших в выборку. Линейная зависимость от n недопустима. Квадратичная по m - тоже не допустима. Ну и да, время выполнения - не более 1-5 секунд в типичном кейсе.
  • ВК, COM и прочие: читерством не пользоваться - чистая 1С
  • СУБД - MS SQL, но решение должно быть удовлетворительным и для других СУБД.
  • Размер БД должен расти не больше чем O(n*ln(n)). Уж точно не квадратично.
  • Профиль распределения времени перехода между статусами может поменяться. Система не должна от этого разваливаться. Нагрузка на систему может вырасти кратно. Система не должна от этого разваливаться.

Тому кто первый покажет корректное решение (не обязательно cf, можно словами описать) - 100 $m (всё равно без дела валяются).
SagittariusA; eeeio; user1021675; alk; Liris; JohnyDeath; akR00b; ildarovich; Sergey.Noskov; +9 Ответить
42. Sergey.Noskov 1433 26.08.16 11:37 Сейчас в теме
(40) speshuric, конкурс это клевая идея!
43. Sergey.Noskov 1433 26.08.16 12:23 Сейчас в теме
(40) speshuric,
1. Вывести все заявки в статусах "Новый", "ВРаботе" на момент времени T0. T0 может быть как в начале истории всех заявок, так и в середине и в конце.

на примере, что б не было вопросов:
Заявка1; 01.01.16 Статус = Новый
Заявка1; 02.01.16 Статус = ВРаботе
Заявка1; 10.01.16 Статус = Сделано

Заявка2; 01.01.16 Статус = Новый
Заявка2; 03.01.16 Статус = ВРаботе
Заявка2; 04.01.16 Статус = Сделано

Заявка3; 01.01.16 Статус = Новый
Заявка3; 04.01.16 Статус = ВРаботе
<Заявка3 так и не закрыта>

Т0 = 05.01.16, в выборку должны попасть и Заявка1 и Заявка3, так?
44. user_2010 1046 26.08.16 14:53 Сейчас в теме
(43) только Заявка3, имхо
45. Sergey.Noskov 1433 26.08.16 15:23 Сейчас в теме
(44) user_2010, имхо это было бы слишком просто)) Честно говоря не вижу реальных задача, когда и Заявка1 и Заявка3 должны попадать в выборку, но часто заказчик просит "хочу видеть как было на дату N" и хоть ты тресни - делай.
46. user_2010 1046 26.08.16 15:34 Сейчас в теме
(45) да, ошиблась, вы правы 1 и 3.
48. Allexe8.1 27.08.16 21:13 Сейчас в теме
(46) user_2010, Корнет, вы женщина? )) По сабжу - классная статья, деллину привет. Может попинаете одноэсов, когда они научатся булькать в темпы?
50. Sergey.Noskov 1433 30.08.16 16:07 Сейчас в теме
(48) Allexe8.1,
Алексей, привет!
Может попинаете одноэсов, когда они научатся булькать в темпы?

О чем речь?
52. Allexe8.1 30.08.16 23:28 Сейчас в теме
(50) Извини, может не по адресу, конечно)
Говорю о том, чтобы из внешних источников (условно, ТЗ, стандартно - файлы) - можно было инсертить во временные таблицы не построчно (как оно происходит), а через bulk insert. Просто жалуюсь на самом деле)
53. Sergey.Noskov 1433 31.08.16 11:25 Сейчас в теме
(52) Allexe8.1, эт на партнерский форум надо, описать что хочешь и зачем конкретно надо. Помимо ответа "пожелание записано", есть шанс получить подсказку от сообщества, например "булькать" во внешние источники данных ;)
Allexe8.1; +1 Ответить
54. speshuric 1343 01.09.16 00:21 Сейчас в теме
Извиняюсь, что долго не отвечаю.
(43) Конечно, 1 и 3
(49) Вы верно ухватили мысль, что стандартные индексы B-Tree не решают эту задачу без костылей. Полезные кейворды "interval tree", "spatial index" (второе в чистом виде обычно для геометрии/географии, но есть применения и для интервального поиска). К сожалению, на первый взгляд у меня есть сомнения, что ваша реализация сработает, посмотрю внимательнее, прокомментирую могу ошибаться.
(51) Вы нашли неплохое решение (фактически это фильтрованные по неконечным значениям итоги для регистров сведений). Сам я решал близким способом. Но в вашем варианте неочевидно решение для "среднее время по заявкам". Опять же - в ближайшее время посмотрю внимательнее.
55. ildarovich 8065 01.09.16 03:35 Сейчас в теме
(54) speshuric, поясню чуть-чуть свое решение:

Пусть начало и конец интервала представлены строковым представлением времени.
Если начало и конец укладываются в одну секунду, то интервал получает индекс - полное представление времени с секундами,
Иначе если начало и конец укладываются в десятку секунд, то интервал получает индекс в виде представления времени, где на месте единиц секунд - прочерк,
Иначе если начало и конец укладывается в минуту, то интервал получает индекс, где на месте секунд - два прочерка,
Иначе если ... в десяток минут, то ... на месте единиц минут и секунд - три прочерка,
...
Иначе если ... в один год, то ... на месте месяца, дня, часа, минут, секунд - прочерки
и так далее.
Для поиска интервалов конкретного состояния, включающих заданный момент, достаточно поискать его в интервалах секунд (будет полное совпадение), десятков секунд (будет совпадать при простановке прочерка на место единиц секунд), минут, десятков минут, часов и так далее. Хорошо бы еще LIKE тут было бы использовать, пока думаю над этим.

Наверное, понятно, что более аккуратным будет использовать не строковое, а двоичное представление моментов времени, как в (49).
В базе данных к таблице регистра сведений добавляется только один реквизит - индекс интервала, и поисковой индекс строится по состоянию и этому дополнительному реквизиту. Который меняется один раз при записи следующего состояния этой заявки. Этот индекс позволяет определить заявки, актуальные в заданный момент времени (точнее, существенно сузить область поиска), и далее уже решать стандартным образом все остальные задачи.

Решение из (51), как мне кажется, нарушает условие квадратичной зависимости объема. Так как если будет хотя бы один процент заявок, зависших не в конечном состоянии, то база будет расти как n * n / 100.

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

Например - один независимый непериодический регистр сведений для текущего статуса заявок. Измерение - заявка, ресурсы: состояние и момент его начала. И регистр накопления для истории. С измерениями: заявка, статус, ресурсом количество - число(1, 0). При смене статуса делается две записи: одна +1 в момент начала предыдущего статуса, вторая -1 в момент начала нового. Здесь не нужно никаких особых состояний, регламентных заданий. Период итогов выбирается исходя из статистики смены состояний (интересен расчет оптимума). Среднее тогда можно посчитать как в статье Расчет средних по периодам в запросе - это элементарно!
56. MaxMNSH 01.09.16 11:46 Сейчас в теме
(55) ildarovich,
Насчет роста базы, вы правы, там возможно не так все просто (зависимость n*ln(n) надо еще доказать), попробую позже дать более точную оценку (там все сложнее так как 1% заявок зависает дольше 30 дней но не висит бесконечно, процент зависших бесконечно или долее 30 непонятен и все зависит от времени, поэтому формула роста будет, думаю, несколько другого вида чем n*n/100)

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


64. Ovrfox 14 02.09.16 11:19 Сейчас в теме
(55) ildarovich, Я поддержу Вас. Действительно, подобная задача (при правильной постановке) оптимально решается регистром накопления
Объясню свое уточнение - при правильной постановке. К сожалению, случайное изменение статусов (типа могло быть в статусе "приостановлен", а могло и не быть) не позволяет нормально использовать механизм регистра остатков.
Т.е. при переходе в новый статус, предыдущий должен быть "определябельным" по новому статусу. Тогда механизм не нарушится.
Иначе может быть ситуация, когда задача завершается. Ее переводят в статус завершено, но "случайно" не провели перевод из статуса "новый" в "в работе".
Тогда в регистре остатков появляется запись - "новый" + "завершено", а при проведении случайно не проведенного документа либо -"Новый" + "в работе", что лучше, чем -"завершено" + "в работе".
В варианте строгого рабочего потока, даже если статес завки "новый" и ее завершают, то проводка будет -"в работе" + "Завершено". И по остаткам будет видно, что заявка находится одновременно в ситатусах новый и завершено, и даже "в работе", но со знаком минус. Что явно указывает на пропущенный этап.
73. MaxMNSH 02.09.16 16:54 Сейчас в теме
(55) ildarovich,

Попробую, как и обещал, дать оценку роста более точно

берем 8640 заявок в день (каждые 10 секунда создается заявка)
берем 3 статуса
Берем 366 дней
берем 10 лет
берем настройку периода N дней = 31

8640*3*366*10 = 95 милионов, округлим до 100 млн

Далее считаем численно рост базы за 10 лет для самого плохого случая (когда 0.01 заявок висят в 2х неконечных статусах более 30 дней, самых плохой вариант висят бесконечно)

считаем сразу, опять самых плохой вариант что у нас на 0 Лет уже есть 100 млн заявок из которых 0.01 зависло более 31 дня на бесконечность (берем так потому что за 10 лет у нас их больше стать не может и если возьмем сразу 100млн то худшего варианта нет)

получаем наибольший рост объема за 10 лет как: n + n*0,01(процент)*2(статуса)*120(месяцев, т.е. каждый месяц количество записей добавляется из-за зависших)

подставляем 100млн + 100млн*0,01*2*120 = 100млн + 240млн = 340 млн (это самых плохой случай, реально будет всегда меньше)

теперь берем n*ln(n) для 10 лет, получим 100млн * ln(100млн) = приближенно 100млт * 18 = 1800 млн

т.е. на участке 10 лет мы ТОЧНО уложимся в зависимость n*ln(n) для регистр2

при тех же нач условиях если посчитать, это сохранится более 50 лет (для 50 лет худший вариант 6 500 млн записей, для n*ln(n) это 10 000млн записей), что рост будет меньше чем n*ln(n)

Далее уже может стать больше чем n*ln(n), но зависимость не квадратичная. А участок порядка 50 лет нам гарантирован, что мы уложимся в зависимость из условия задачи

При уменьшении Nдней конечно же рост будет увеличиваться пропорционально N и статистики перехода статусов для этого N

Полную формулу роста вывести тут не просто, поэтому ограничился здесь численным расчетом гарантированного периода для N = 31 дней
77. Ovrfox 14 02.09.16 17:23 Сейчас в теме
(73) MaxMNSH, Вы посчитали только дополнительный рост регистра, а основной рост почему не учли?
81. MaxMNSH 02.09.16 17:56 Сейчас в теме
(77) Ovrfox,

100 млн + 100млн*0,01*2*120

здесь первое слагаемое 100 млн и есть основной рост (если бы не было дублирующих записей регламентым заданием то регистр за 10 лет дорос бы до 100 млн записей)

другой вопрос что в моем решении у нас еще есть таблица обычного периодичесткого регистра = это еще + 100 млн и так же таблица где хранятся заявки для регламентного задания это еще 100*2 = 200млн максимум

т.е. в этом случае получим для 10 лет суммарно 640 млн записей в 3х таблицах с очень большим избытком от реальности.

я считал рост только регистра2 решения так как вопрос был по нему.

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

но если добавить туда и саму таблицу заявок и обычный периодический регистр и таблицу регистрации заявок то все равно уложимся в гарантированный срок 50 лет при заданных нач условиях
101. speshuric 1343 07.09.16 23:40 Сейчас в теме
(55)
1. Я идею-то понял. В ней есть "особенность". У вас кортежи, переходяще через "большие" границы начинают мощно замусоривать выборку. А если учесть технические детали (типа того, как устроена статистика MS SQL), то ваше ГДЕ (s, it) В (...) начнет валиться в сканы. Хотя сама идея очень близка к spatial indexes.
2. Да, решение в (51) имеет эту квадратичную составляющую, но я готов её "простить" на сроке жизни решения.
3. Регистры накопления использовать можно, но там есть важная техническая гадость. Итоги придётся пересчитывать. Пересчитывать часто и больно, иначе в них будет много "нулевых" записей и смысла в них не будет. А еще это решение очень fragile относительно изменений и относительно сбоев (изменения задним числом для такой системы могут быть фатальными). И итоги только "помесячно" (что сильно ограничивает область применения). Помимо проблемы вечных пересчетов и последовательностей, есть еще проблема "эй, а какое состояние было?", которая приводит к замедлению проведения и потенциально к серьёзным проблемам параллельной работы (это, считай, контроль остатков прикрутить надо). Ну или эти движения нужно достаточно хитро создавать регламентным заданием. Овчинка выделки не стоит. Это же могу адресовать и (64), (66).

(66)
Решение было бы хорошим, но у него несколько важных проблем.
1. Регистр остатков. См. выше.
2. Регистр оборотов. Решение рабочее с оговорками про "пустые" итоги и про пересчеты.

(70) Нет, никаких косвенных указаний о структуре в задаче не было.
(76) Регистр остатков записывает адски больше записей. Не забывайте, что при записи приход-расход итоговая запись (по крайней мере до 8.3) с нулевым итогом остаётся. И это существенно.

(93) и всем кто неправильно считает O(...). O(f(n)) = O(k*f(n)), если k - константа. Квадратичная зависимость в решиении с "итогами" есть (если не строить более хитрых конструкций), это решение по O() хуже, чем O(n*ln(n)) но, она действительно в практическом смысле допустима, хоть и формально нарушает условие.

Я предлагаю отдать призовой фонд MaxMNSH - хорошее практическое решение, близкое к оптимальному.
Однако у ildarovich - подмечены ключевые проблемы задачи, предложенное решение весьма оригинально (хотя и в MS SQL работать не сможет хорошо).
Sergey.Noskov был вне конкурса и вообще вне зависимости от результатов он выиграл от дискуссии к статье :)
Если кто-то считает что я несправедлив, то наверное он прав.

Собственно, вся эта задача была призвана показать, что несмотря на простоту регистров сведений, есть задачи, которые классически принято решать с РС (ну срез статусов-то классика), а на практике не всё так просто.
102. Ovrfox 14 08.09.16 10:48 Сейчас в теме
(101) speshuric,
3. Регистры накопления использовать можно, но там есть важная техническая гадость. Итоги придётся пересчитывать. Пересчитывать часто и больно, иначе в них будет много "нулевых" записей и смысла в них не будет. А еще это решение очень fragile относительно изменений и относительно сбоев (изменения задним числом для такой системы могут быть фатальными). И итоги только "помесячно" (что сильно ограничивает область применения). Помимо проблемы вечных пересчетов и последовательностей, есть еще проблема "эй, а какое состояние было?", которая приводит к замедлению проведения и потенциально к серьёзным проблемам параллельной работы (это, считай, контроль остатков прикрутить надо). Ну или эти движения нужно достаточно хитро создавать регламентным заданием. Овчинка выделки не стоит. Это же могу адресовать и (64), (66).

1. Что касается 0-вых остатков - да, они есть, но их относительно немного, особенно с учетом того, что 80% заявок завершается за 5 дней. Согласно моим подсчетам менее 1.3%
Т.е пересчитывать не нужно.
2. пересчет последовательности и хрупкость для определения предыдущего состояния - это именно мое замечание. И он обходится, если мы можем всегда ответить на вопрос, какое было предыдущее состояние без запроса к регистру. Как я и предлагал
3. Остается проблема изменений задним числом, которая работает, хоть и тяжко в варианте с регистром остатков.
Вопрос как эта проблема решается в других предложенных системах? Полным перебором истории? Разве это лучше , чем в варианте с регистром остатков?
Это лирика
А вообще с Вашим выбором решения я согласен. Оно самое компактное. (по кол-ву записей)
103. Sergey.Noskov 1433 08.09.16 14:32 Сейчас в теме
(102) Ovrfox,
1. Что касается 0-вых остатков - да, они есть, но их относительно немного, особенно с учетом того, что 80% заявок завершается за 5 дней. Согласно моим подсчетам менее 1.3%
чет мало насчитали...
Например, создали заявку, сделали запись "Приход" Статус=Новый, Заявка=№1. Через 30 минут заявку взяли в работу, т.е. добавляем запись "Расход" для Статус=Новый, Заявка=№1 и запись "Приход" Статус=ВРаботе, Заявка=№1. Еще через сутки заявку сделали, т.е. еще запись "Расход" для Статус=ВРаботе, Заявка=№1. Таким образом в итогах останутся две строки и обе с количеством 0. В сутки минимум будет появляться 80% от новых заявок, т.е. 6912 нулевых записей, плюс еще закрытые и заявки прошлых дней. Скорее всего не меньше 10 000 не нужных пустых строчек.
105. Ovrfox 14 09.09.16 12:11 Сейчас в теме
(103) 0-вые текущие не переносятся при пересчете остатков на следующий месяц
В 0-вых старых остануться только те записи, которые корректировались задним числом или при переходе через период расчитанных итогов.
Вот я только их и учитывал.
104. Sergey.Noskov 1433 08.09.16 16:42 Сейчас в теме
(101) speshuric,
Я предлагаю отдать призовой фонд MaxMNSH - хорошее практическое решение, близкое к оптимальному.
Ок, с победителем определились! Кстати, обязательно ждем эталонного решения ;)
Хочу предложить еще один вариант, думал как решить задачу используя только одну таблицу и при этом достаточно оптимально с точки зрения получения данных. Это, есессно, крайность и при строгой трактовке требований задачка получается не решаемой, но тем интереснее был поиск решения.
Получилось следующее: независимый, непериодический регистр сведений с измерениями: Статус; ДатаСменыСтатуса; ДатаЗаписи; Заявка. Ресурсов и реквизитов нет. Отвечаю на вопрос «Почему не периодический?» – просто, что б получить более оптимальный индекс т.к. стандартными средствами получить индекс [Статус; ДатаСменыСтатуса; Период] проблематично.
Принцип записи: При изменении статус добавляем новую строку, в которой ДатаСменыСтатуса=01.01.3999, ДатаЗаписи=ТекущаяДата(). При этом, находим строку регистра с предыдущим статусом и обновляем у неё ДатаСменыСтатуса= ТекущаяДата(). Таким образом, если у записи ДатаСменыСтатуса=01.01.3999, то статус этой записи является текущим статусом заявки. Тексты запросов будут при этом такими:
Задачка 1:
ВЫБРАТЬ
	Заявки.ДатаЗаписи,
	Заявки.Заявка,
	Заявки.Статус
ИЗ
	РегистрСведений.Заявки КАК Заявки
ГДЕ
	Заявки.Статус в (&Новый, &ВРаботе)
	И Заявки.ДатаСменыСтатуса > &Т0
	И Заявки.ДатаЗаписи <= &Т0
Показать
это слабое место данного решения т.к. чем глубже в истории мы выбираем точку Т0, тем больше данных мы будем читать, но за «минимализмы» чем то приходится платить. Хотя если Т0 не залезает глубже недели-двух, то вариант вполне можно рассматривать как рабочий.
Задачка 2:
ВЫБРАТЬ
	СРЕДНЕЕ(ДанныеОбработки.ВремяОбработки) КАК ВремяОбработки
ИЗ
	(ВЫБРАТЬ
		СУММА(РАЗНОСТЬДАТ(Заявки.ДатаЗаписи, &ДатаКон, МИНУТА)) / КОЛИЧЕСТВО(*) КАК ВремяОбработки
	ИЗ
		РегистрСведений.Заявки КАК Заявки
	ГДЕ
		Заявки.Статус = &Новая
		И Заявки.ДатаСменыСтатуса = &Дата01013999
		И Заявки.ДатаЗаписи <= &ДатаКон
	
	ОБЪЕДИНИТЬ ВСЕ
	
	ВЫБРАТЬ
		СУММА(РАЗНОСТЬДАТ(Заявки.ДатаЗаписи, Заявки.ДатаСменыСтатуса, МИНУТА)) / КОЛИЧЕСТВО(*)
	ИЗ
		РегистрСведений.Заявки КАК Заявки
	ГДЕ
		Заявки.Статус = &Новая
		И Заявки.ДатаСменыСтатуса МЕЖДУ &ДатаНач И &ДатаКон) КАК ДанныеОбработки
Показать


106. Ovrfox 14 09.09.16 12:16 Сейчас в теме
(104) Для попадания в ваш индекс запрос к задаче 1 нужно передалть примерно так
ВЫБРАТЬ
    Заявки.ДатаЗаписи,
    Заявки.Заявка,
    Заявки.Статус
ИЗ
    РегистрСведений.Заявки КАК Заявки
ГДЕ
    Заявки.Статус = &Новый
    И Заявки.ДатаСменыСтатуса > &Т0
    И Заявки.ДатаЗаписи <= &Т0
объединить все
ВЫБРАТЬ
    Заявки.ДатаЗаписи,
    Заявки.Заявка,
    Заявки.Статус
ИЗ
    РегистрСведений.Заявки КАК Заявки
ГДЕ
    Заявки.Статус = &ВРаботе
    И Заявки.ДатаСменыСтатуса > &Т0
    И Заявки.ДатаЗаписи <= &Т0
Показать
107. Sergey.Noskov 1433 12.09.16 01:01 Сейчас в теме
(106) Ovrfox, т.е. в моем варианте в индекс не попадет?
108. Ovrfox 14 12.09.16 10:18 Сейчас в теме
(107) Скорее всего не попадет. Можно попробовать отследить план в профайлере.
А вообще условия вида Парам в (Список) не попадают в индекс. Нужно делать внутренее соединение, чтобы пападать.
109. Sergey.Noskov 1433 12.09.16 13:11 Сейчас в теме
(108) Ovrfox, т.е. вы даже не проверили, но советы советуете? Понимаете, что вот кто то прочитает, так сказать "услышав звон и не поняв про что речь" и в дальнейшем будет постоянно ошибаться.
А вообще условия вида Парам в (Список) не попадают в индекс. Нужно делать внутренее соединение, чтобы пападать.
Антинаучно. Суть процесса одна - получить данные из таблицы с фильтрацией по нескольким значениям. Только в первом случае значения передаются списком параметров, во втором - таблицей.
Прикрепленные файлы:
110. Ovrfox 14 12.09.16 13:53 Сейчас в теме
(109) Дык если я прав, то в чем антинаучность?
В том , что я не расписал о том, что на самом деле виноват оптимизатор SQL? Что если запросы писать напрямую на SQL , то я точно не прав?
А какая разница, если для 1С выполняется то, что я сказал?
Кстати, речь шла о индексе, состоящем из более чем одного поля, в котором одно из полей используется в условии с "в".
111. Sergey.Noskov 1433 12.09.16 15:05 Сейчас в теме
(110) Ovrfox, я привел три плана запроса и во всех них используется тот же самый индекс (состоит из 4х полей). Запросы написаны не в SQL (что заметно в использовании конструкции exec sp_executesql), а просто отловлены в профайлере в момент выполнения в консоли запросов. Заскринить план в профайлере или поверите наслово, что он такой же?
Почему не научно, тож объяснил.
Нельзя давать такие советы, особенно на простых запросах как в (104). В оптимизации запросов вообще мало по истине универсальных советов и уж точно отказ от IN к ним не относится.
112. Ovrfox 14 12.09.16 15:49 Сейчас в теме
(111) Я правильно понял, что Вы утверждаете, что для составного индекса (В вашем примере он не составной, но замедления все равно нет) ВСЕГДА будет попадание в индекс, если часть условия на индекс находится в конструкции "В", а часть в простом условии на больше меньше?
Т.е. Вы не согласитесь со мной (к сожалению, я и не привожу конкретных ссылок, подтвержлдающих мои слова), что оптимизатор SQL в таком случае может ошибится и не использовать индекс. А для 100% попадания в индекс лучше использовать простые операторы странения.
Я советую Вам почитать форум MS и самостоятельно найти подтверждение моим словам и ли привести пример, когда мой совет может привести к плохому результату ( Вариант к тамоу же как и оператор "в" -не подходит, разве что вы докажете , что это будет в 100% случаев)
113. Sergey.Noskov 1433 12.09.16 19:44 Сейчас в теме
(112) Ovrfox, Олег, я всего лишь прошу аргументировать, а вместо этого получаю еще один совет. Попадание в индекс продемонстрировано.
У меня большая практика оптимизации и запросы, в которых отказывались от IN, так же встречались, но это было вызвано сложностью самого запроса (много условий и таблиц), иногда характером распределения данных, поэтому я б не рискнул давать такие рекомендации без анализа ситуации. Даже для одинакового запроса, но на различных данных, SQL может компилить разные планы.
Если вам известно об ошибках в SQL, желательно привести ссылки или описать проблемы подробнее, указав версию SQL и порядок воспроизведения.
114. speshuric 1343 12.09.16 21:12 Сейчас в теме
(112) Ovrfox,
Вы зря спорите. Даже зная структуру и запрос нельзя наперед сказать, будет ли использован индекс. В этом смысле приведенный код
Заявки.Статус в (&Новый, &ВРаботе)
    И Заявки.ДатаСменыСтатуса > &Т0
    И Заявки.ДатаЗаписи <= &Т0
вполне имеет шансы задействовать индекс (Статус, ДатаСменыСтатуса), если ДатаСменыСтатуса не сильно древняя. MS SQL обычно вполне справляется и с вложенными запросами, и с IN, и с EXISTS, если, конечно, семантика одинаковая. Это сильно зависит от частотной статистики, работы cardinality estimator (который в 2014 поменяли, кажется), от parameter sniffing и еще от горы вещей. И соединение тут ни фига не панацея, а иногда даже яд. Мне вот прям десятки раз приходилось на конкретных примерах показывать, почему (в том конкретном случае) надо использовать вложенные запросы, "В" вместо соединения, "ИЛИ" вместо ОБЪЕДИНИТЬ ВСЕ, и условие ГДЕ вместо параметров виртуальных таблиц. То есть практически всё что рассказывал уважаемый К. Рупасов, только наоборот. Проблема лишь в том, что это рекомендации для конкретной ситуации.

В отношении "Поле В (&Парам1, &Парам2)" лично я бы пошёл именно по пути Sergey.Noskov - написал корректный семантиически запрос с "В", и лишь потом, если он ведёт себя не так, как ожидается - попробовал "оптимизировать": через ОБЪЕДИНИТЬ ВСЕ или другими приемами.
115. Ovrfox 14 13.09.16 09:43 Сейчас в теме
(114) speshuric, Все, что я сказал - это дал рекомендацию, когда для условия в используется два значения и есть дополнительное условие по второй части индекса, то рекомендовал использовать условие на равенство и объединить все. Протому что в таком варианте записи запроса мы однозначно попадем в индекс, но даже если и условие "В" тоже попадет в индекс, то мы не проиграем в производительности.
Т.е. я рекомендовал решение, которое в некоторых случаях ускорит запрос, а в некоторых его НЕ ЗАМЕДЛИТ.
Что касается сложных запросов в условии "В", то я так же, по своему собственному опыту работы с 2005 и 2008 серверами, пришел к выводу, что часто условие "В" замедляет запрос, а полное соединение нет.
Уважаемый оспорил мой опыт и сказал. что он неверен, т.е. его использовать нельзя. Я просто попросил привести пример, когда нельзя использовать, т.е. когда он приведет к замедлению запроса. К сожалению, уважаемый даже не смог привести правильный пример, иллюстрирующий упомянутое мнение.
Поэтому голословными утверждениями занимается уважаемый Сергей, а я просто пытаюсь его успокоить, чтобы он не спорил по пустякам, особенно когда мои рекомендации работают, а он рекомендаций вообще не дает.
116. Ovrfox 14 13.09.16 09:53 Сейчас в теме
(114) speshuric, кстати, меня заинтересовал вариант, когда "объединить все" и условие равенства дает худший результат, чем условие вхождения в список.
До сих пор я был уверен, что это как минимум не хуже. Более того, я помню, что читал статью, где был описан один из способов оптимизации запросов внутри MS SQL. И там было сказано, что в некоторых случаях именно так и преобразовывается запрос . Т.е. Вы просите MS SQL использовать условие "В", оптимизатор думает и принимает решение использовать равенство и "объединить все".
124. ildarovich 8065 20.09.16 12:51 Сейчас в теме
(104)
Хотя если Т0 не залезает глубже недели-двух, то вариант вполне можно рассматривать как рабочий
Проводил эксперименты с выборкой половины из двух миллионов записей по условию "<" с не кластерным индексом. Оказалось, что MSSQL делает это меньше, чем за полсекунды. Так, что неделя - если за нее приходит порядка миллиона заявок, а если во всей базе заявок меньше миллиона (так в большинстве случаев), то беспокоится вообще не о чем - не нужно вообще ничего дополнительного придумывать (даже ДатаСменыСтатуса добавлять).

Из этого как будто бы следует, что серьезность проблемы (всей этой "интересной" задачи) оказалась сильно преувеличена. Рост времени выполнения запроса, с которым нужно бороться, проявляется (в MS SQL) в таблицах с многими миллионами записей. А много ли реальных практических задач с такими количествами записей в периодических регистрах сведений?

Это я к тому, что решение, которое я предложил в (49) и объяснял в (55) оказалось вполне рабочим, но вот досада - чтобы показать его преимущества на MSSQL приходится генерировать таблицы с многими миллионами записей.
125. Sergey.Noskov 1433 20.09.16 13:59 Сейчас в теме
(124) ildarovich,
...не нужно вообще ничего дополнительного придумывать (даже ДатаСменыСтатуса добавлять)
Речь о "классическом" регистре сведений Период/Заявка/Статус?
126. ildarovich 8065 20.09.16 14:23 Сейчас в теме
(125) да, как будто при двух и меньше миллионах записей выбрать заявки с нужным статусом на конкретную дату из периодического регистра сведений с измерением - заявка и ресурсом статус - не проблема. Тесты как будто не должны замечать разницы времени выборки из 0,4; 0,8; 1,2; 1,6; 2,0 миллиона записей.
Это я так думаю по результатам своего эксперимента с таблицей Ключ, НачалоИнтервала, КонецИнтервала, Значение. Так вот,
ВЫБОР * ИЗ Дано ГДЕ &ВыбранныйМоментВремени МЕЖДУ НачалоИнтервала И КонецИнтервала
работает даже на миллионе записей слишком быстро, чтобы что-то изобретать.
127. Sergey.Noskov 1433 20.09.16 15:02 Сейчас в теме
(126) ildarovich, не все так просто, если мы говорим про классический регистр, мы не можем ограничивать период слева т.к. не знаем дату первого события и для, того, что бы отфильтровать по статусу на дату, придется сначала сделать полный срез, что то вроде:
Выбор * из РегистрСведений.СтатусыЗаказов.СрезПоследних(&Т0,) как Т1 ГДЕ Т1.Статус в (&Статусы)
думаю очевидно, что этот запрос прочитаем все строки таблицы с периодом <= Т0.
В случае с <<..ГДЕ &ВыбранныйМоментВремени МЕЖДУ НачалоИнтервала И КонецИнтервала...>> ситуация другая, мы сразу ограничиваем читаемые строки и выбираем только те, где НачалоИнтервала>=&ВыбранныйМоментВремени. Если &ВыбранныйМоментВремени каждый раз будет равноудаленной от текущей даты, то число попадаемых в условие строк почти всегда будет одинаковым, а в первом варианте - чем дольше живем, тем больше чтений.
128. ildarovich 8065 20.09.16 21:43 Сейчас в теме
(127) я это хорошо понимаю, даже приходилось применять решение типа (104). Оно мне кажется довольно симпатичным: чем дальше по времени мы хотим заглянуть в историю, тем больше времени должны потратить: своего рода мягкий вариант архивации.

Я хотел донести другую мысль, но сначала ответьте:
Выбор * из РегистрСведений.СтатусыЗаказов.СрезПоследних(&Т0,) как Т1 ГДЕ Т1.Статус в (&Статусы)
думаю очевидно, что этот запрос прочитаем все строки таблицы с периодом <= Т0
- сколько примерно времени по вашему придется потратить, чтобы прочитать
все строки таблицы с периодом <= Т0
, если в таблице целых ДВА МИЛЛИОНА записей, а момент Т0 в середине истории. Какое число приходит в голову, если не проводить измерений? Сколько секунд хотя бы примерно? Насколько пугающим является это число? И стоит ли бороться за уменьшение этого времени?

PS: Кстати, чтобы не ломать структуру обычного регистра в вашем предложении, можно использовать такой ход - назначить каждому статусу свой столетний (например) диапазон, сделать ДатаСменыСтатуса реквизитом (индексированным), задавать его для каждого статуса в своем диапазоне. Тогда в одном поле фактически будут объединены поля Статус и ДатаСменыСтатуса и будет работать штатный индекс реквизита в проверке Статус = Х И ДатаСменыСтатуса < Т0.
129. Sergey.Noskov 1433 26.09.16 12:28 Сейчас в теме
(128) ildarovich, Идея же, глобально, не в том, как быстро выполнится запрос, а в том, как эффективно он тратит ресурсы. Даже секунда это может бы очень много, например, если этот запрос выполнит 100 юзеров, то потребуется почти 100 секунд процессорного времени и на 80ти ядерной машине вы получите нагрузку на CPU в 100%, что приведет к заметной деградации производительности всех пользователей. Второй важный момент - архитектура должна обеспечивать скорость работы не зависимо от продолжительности ведения учета. Из своей практики могу сказать, что рост может быть экспоненциальным и 2млн строк к концу года легко превращаются в 100млн.

(122) speshuric, заждались уже))
117. Ovrfox 14 13.09.16 14:51 Сейчас в теме
(101) speshuric, Что же Вы призовой фонд зажали? А как же обещание выдать его MaxMNSH?
118. Sergey.Noskov 1433 13.09.16 15:00 Сейчас в теме
(117) Ovrfox, Это моя ошибка, отметил лучшим ответом комент speshuric'а. Исправимся.
165. sss999 50 08.09.22 23:24 Сейчас в теме
(55) Подскажите если я пишу через набор в регистр сведений независимый непериодический, он весь блокируется? я находил эту инфу ранее видел.
72. MaxMNSH 02.09.16 15:53 Сейчас в теме
(54) speshuric,

Поясняю по поводу расчета среднего времени перхода м-ду Статус1 и Статус2, привожу пример реального запроса. (насчет общей оптимальности его отдельная тема, но первоначальные выборки во временный таблицы у нас ВСЕГДА ограничены по ДатаЗаписи и следовательно время его работы слабо зависит от общего объема БД)

ВЫБРАТЬ РАЗЛИЧНЫЕ
Регистр2.Заявка КАК Заявка,
Регистр2.ДатаУстановкиСтатуса КАК ДатаУстановкиСтатуса
ПОМЕСТИТЬ ВТСтатус1
ИЗ
Регистр2 КАК Регистр2
ГДЕ
Регистр2.ДатаЗаписи МЕЖДУ &НачПериодCУчетомNДней И &КонПериодCУчетомNДней
И Регистр2.Статус = &Статус1
И Регистр2.ДатаУстановкиСтатуса <= &ДатаКон
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
Регистр2.Заявка КАК Заявка,
Регистр2.ДатаУстановкиСтатуса КАК ДатаУстановкиСтатуса
ПОМЕСТИТЬ ВТСтатус2
ИЗ
Регистр2 КАК Регистр2
ГДЕ
Регистр2.ДатаЗаписи МЕЖДУ &НачПериодCУчетомN И &КонПериодCУчетомN
И Регистр2.Статус = &Статус2
И Регистр2.ДатаУстановкиСтатуса МЕЖДУ &ДатаНач И &ДатаКон
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
СУММА(РАЗНОСТЬДАТ(ВТСтатус1.ДатаУстановкиСтатуса,
ЕСТЬNULL(ВТСтатус2.ДатаУстановкиСтатуса, &ДатаКон), СЕКУНДА))
/ КОЛИЧЕСТВО(ВТСтатус1.Заявка) КАК СреднееВремяПереходаСтатус1ВСтатус2
ИЗ
ВТСтатус1 КАК ВТСтатус1
ЛЕВОЕ СОЕДИНЕНИЕ ВТСтатус2 КАК ВТСтатус2
ПО ВТСтатус1.Заявка = ВТСтатус2.Заявка
49. ildarovich 8065 30.08.16 11:38 Сейчас в теме
(40) speshuric, задача очень интересная. Жалко, что запрограммировать, наверное, не успеваю.

На уровне идеи:

Основной проблемой здесь является поиск заявок с конкретным состоянием на заданный момент времени.
Дело в том, что при стандартном подходе приходится либо определять состояние ВСЕХ заявок на заданный момент времени,
а затем выбирать среди них с нужным состоянием, либо определять время состояния заявок,
а затем проверять: сохраняется ли это состояние на интересующий нас момент.
И в том и другом случае приходится выполнять объем вычислений, пропорциональный длине истории.
Что и является проблемой.

В общем случае такая проблема возникает, если таблица хранит записи:
<id,t1,t2,s>, где id - идентификатор заявки, t1 - начало действия состояния, t2 - конец действия состояния,
s - состояние и нужно найти все записи, актуальные на момент tx.

Интересно, как такая задача решается в умных книжках. Не заглядывая в них, предлагается решить эту задачу способом "масштабирования".
Его суть в использовании для выбора интервалов, содержащего точку, определенного МАСШТАБА представления интервала,
при котором его начало и конец "сливается". Как будто мы смотрим на ось времени издалека.
При одном и том же масштабе в ту же точку сольется и заданный момент и поиск можно вести на равенство.
Интервалы разных размеров потребуют разного масштаба.
Поэтому при поиске потребуется проверить несколько (ln n) значений масштаба.

Например, пусть интервал занимает от 11101110111 до 11101111000 (в двоичном представлении - старшие разряды слева).
Тогда ему нужно приписать индекс (it) 1110111----.
Если интервал занимает диапазон 1100011111 до 1101011111. Тогда он получит индекс 110-------.
Проще говоря интервал определяем неизменной левой частью двоичного (или десятичного) представления начала и конца периода.
Для поиска интервалов, актуальных для 1100011011, например, потребуется поискать среди интервалов с индексами:
1100011011, 110001101-, 11000110--, 1100011---, 110001----, 1---------, ----------.


Получается, регистр сведений дополняем реквизитом "индекс интервала".
Открытый интервал имеет индекс интервала: ------------.
При смене состояния следует изменить реквизит в предыдущей записи по этой заявке,
вычислив его (не в запросе) исходя из начала и конца предыдущего состояния.
Трогаем только одну конкретную запись регистра.

Для поиска по заданному моменту и состоянию находим все подходящие индексы интервалов
и используем условие ГДЕ (s, it) В (&ТаблицаПоиска),
где ТаблицаПоиска = (sx, it1)...(sx, itN)).
Массив индексов интервалов (it1,...,itN) рассчитывается вне запроса функцией,
заменяя в цикле прочерками правые цифры представления момента tx.

Возможно, еще есть какие-то технические детали, требующие уточнения, но для кодирования, мне кажется, тут данных достаточно.
speshuric; necropunk; +2 Ответить
130. ildarovich 8065 28.09.16 11:26 Сейчас в теме
В защиту своего решения из (49) пришлось провести небольшое исследование и написать по его результатам статью "Простой метод индексирования интервалов".
В статье рассмотрена более общая задача, но также и задача из (40), как частный случай более общей задачи.
К статье прилагается каркасная конфигурация с обработками заполнения базы данными по условию задачи (40), отчетами, решающими задачи 1 и 2 типовым методом и предлагаемым в (49).
Для трех лет в базе получилось 10 миллионов заявок. Типовой метод работает по первой задаче 16 сек, предлагаемый - 0,59 сек. Вторая задача решается методом из (49) примерно 1,5 секунды (для недельного интервала).
Приглашаю ознакомиться со статьей всех, кто интересовался конкурсом и задачей из (40).
eeeio; molodoi1sneg; shootnik; MaxMNSH; Ovrfox; speshuric; Sergey.Noskov; necropunk; +8 Ответить
51. MaxMNSH 30.08.16 17:46 Сейчас в теме
(40) speshuric,

Предлагаю на обсуждение следующий концепт(идею) решения:

Первый регистр сведений "Регистр1" простой периодический для истории

Период
Заявка
Статус

Дополнительный регистр сведений "Регистр2" не периодический помимо обычного с историей

Измерения:
ДатаЗаписи
Статус
Заявка

Ресурс:
ДатаУстановкиСтатуса


Константа в которой настроим конечный статус заявок. В нашем случае в ней статус "Сделано"

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

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

РегламентноеЗадание работает и выбирает зарегистрированые заявки:

Если заявка в текeщем статусе более N Дней, то пишем в регистр2 ее еще раз с ДатаЗаписи = ТекДата, ДатаУстановкиСтатуса = Та
дата когда статус реально был установлен, и сам статус = тек статус
(N дней = Настройка общая для робота, можно хранить в другом периодическом регистре что бы была возможность гибко поменять
настройку и знать какое количество дней было на любую дату)

Т.е. в течении всего времени пока заявка в одном и том же статус она каждые N дней отписывается в регистр2 (добавляется новая
запись ДатаЗаписи = ТекДата на момент записи) о том что статус сохранился.

Итого получаем, что в запросах на анализ переходов статусов Новый – ВРаботе (в общем виде м-ду любыми двумя статусами кроме конечного), а так же в запросе всех заявок в статусах новый/Вработе на произвольную дату

у нас будет ВСЕГДА ограничение первичной выборки по


ИЗ Регистр2 КАК Регистр2

ГДЕ
ДатаЗаписи МЕЖДУ &НачПериода И &КонецПериода (выборка всегда конечна и ее размер не сильно зависит от размера всей таблицы)

+при необходимости условие на &статус

Важно: отбор по периоду в первичной выборке надо увеличивать от дат заданных юзером что бы он был более шага N отписки в регистр повторных записей
А далее фильтровать полученные временные таблицы уже по периоду который задал юзер в отчете &ДатаНач и &ДатаКон по ДатаУстановкиСтатуса

Таким образом мы получим все заявки на нужные даты(или интервал дат) по нужным статусам и можем посчитать как среднее время перехода м-ду статусам так и вывести сами заявки в статусах

Итого получаем

1. Запросы для пунтков 1 и 2 "Какие данные хотим получать" ВСЕГДА будут ограничены условием МЕЖДУ по ДатаЗаписи и
зависимость от общего объема данных будет много меньше линейной (всегда берем только отрезок по датам)

2. пункт 3 "Какие данные хотим получать" получаем из основного периодического регистра ("наивной структуры") историю
ВСЕГДА с отбором по конкретной заявке/списку


3. На статусы мы не завязаны и не хардкодим их программно, пользователи могут добавлять и удалять их, а так же можно сделать
настраиваемым список статусов которые хотят проанализировать пользователи в отчетах п.1 и п.2 "Какие данные хотим получать"

Ограничение: должен быть "конечный" статус заданный в константе. (в примере это статус "сделано").
И данные по нему (какие заявки есть на произвольную дату в этом статусе) мы можем получить корректно только срезом по обычному регистру
истории, но в условии задачи у нас и не указан этот статус для оперативного(быстрого) анализа.

Минусы: Будет избыточность записей в Регистр2 (больше записей чем в регистре истории) но зависимость роста его, при
нормальном подборе N дней, будет меньше чем n*ln(n).

Настраивается параметром "N" дней насколько часто бы будем отмечать заявку в Регистр2. Чем больше N тем
меньше будет увеличение объема от общего числа заявок, Но тем больше будет первичная выборка которая должна захватить
интервал N. Выбирается оптимальный вариант соотношения ОбъемДанных/Количество строк первичной выборки, исходя из статистики
перехода статусов по заявкам и требований к времени выполнения запроса. Например берем 10 дней, 4 дня и т.п.

Настройки можно менять при необходимости по ходу жизни системы в связи с добавлением статусов или при изменении статистики
перехода статусов и нагрузки на базу.
jagon; kaaasteeen; user645801_yyyuuu123q; alk; V4L; Ruden95; Sergey.Noskov; speshuric; +8 Ответить
66. Sergey.Noskov 1433 02.09.16 12:58 Сейчас в теме
(40) speshuric,
раз вариантов мало, напишу некие мысли вне конкурса, это переосмысленное решение "в лоб". Многих могли спугнуть требования вида O(Ln n), O(sqrt (n)) и т.п., думаю было бы больше вариантов, если эти ограничения описать проще.

Итак, возьмем среднестатистического программиста 1С, который не особо углубляется в понимание работы индексов, в то, как именно хранятся данные на уровне СУБД и какие доп затраты возникают при записи того или иного объекта.
В то же время он хорошо понимает функциональное назначение того или иного объекта метаданных и его возможности, разбирается, что виртуальные таблицы это на самом деле подзапрос, хорошо пишет запросы и, как следствие, легко может ответить на вопрос "Почему отбор в параметрах виртуальной таблицы работает быстрее".
Так вот, мне бы хотелось верить, что герой сего комента сможет представить следующее решение:

Задача 1, получение данных на точку Т0, которая, при втором приближении, оказывается задачей "на остаток" - какие заявки не были списаны на дату Т0. Ожидаемое решение - остаточный регистр накопления с измерениями Статус, Заявка и ресурсом Количество. Логика тривиальная - статус Новый добавляет запись с количеством +1, последующие статусы "списывают" текущий остаток по заявке и добавляет новую запись. В процессе окажется, что надо предусмотреть защиту от кривых рук юзера при работе "задним числом" и механизм восстановления последовательности, что, в прочем, решабельно.

Задача 2, среднее время. Опять таки, задача в чистом виде звучит как "получить среднее за период" и очень похожа на задачку "по оборотам". Ожидаемое решение - оборотный регистр накопления с измерениями Статус, Заявка, ресурс ВремяОбработки. Дополнительно создаем агрегат с периодом День и аналитикой Статус. Для покрытия требования 2.1. есть риск свалиться в написание многоэтажных запросов, на деле достаточно организовать запись в регистр следующим образом: при записи новой заявки время обработки рассчитываем на конец текущего дня и каждую ночь увеличиваем его на сутки. При переходе заявки в статус "ВРаботе" сразу рассчитываем время обработки и записываем в регистр, при этом запись со статусом Новый удаляем. Запрос получения среднего за период получается примерно такой:
ВЫБРАТЬ	СУММА(Т3.ВремяОбработки) КАК СреднееВремяОбработки
ИЗ(
ВЫБРАТЬ Т1.ВремяОбработкиОборот - &КонецТекДняМинусДатаКон КАК ВремяОбработки ИЗ РегистрНакопления.СреднееВремяОбработкиЗаказа.Обороты(, &ДатаКон, , Статус = &Новый) КАК Т1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ Т2.ВремяОбработкиОборот ИЗ РегистрНакопления.СреднееВремяОбработкиЗаказа.Обороты(&ДатаНач, &ДатаКон, , Статус = &ВРаботе) КАК Т2) КАК Т3
где параметр &КонецТекДняМинусДатаКон рассчитываем как КонецДня(ТекущааяДата()) - ДатаКон.

Задача 3, история. Есть соблазн запилить регистр "наивной структуры", ну что бы было ;) но мы говорим про умного коллегу, поэтому ожидаемое решение - для истории использовать движения регистра остатков с видом Приход.
Еще раз, это решение - полет фантазии на тему как среднестатистический программист 1С (ок, пусть это буду я без знаний последних 8 лет) мог бы спроектировать идеологически правильную архитектуру. Решение рабочее и, при этом, достаточно быстрое, будет очень близко к временным рамкам 1 секунда на запись и 1-5 сек на чтение.
Минус этой архитектуры в большом количестве накладных расходов связанных с хранением данных (доп таблицы итогов, агрегатов), записью регистров (обновление итогов) и необходимость регламентных операций с достаточно большим объемом данных.
67. Ovrfox 14 02.09.16 13:31 Сейчас в теме
(66) При вашем решении возможно более оптимальное решение.
Для Вашего п.2. достаточно при записи следующего статуса записать в документ, который его устанавливает время действия предыдущего статуса, не заводя регистра оборотов.
И тем более не выполняя регламентных заданий. Тогда
Срез остатков на дату начала даст те заявки, которые находились на указанный момент времени в начальном статусе.
Анализ движений между указанными датами найдет документы перехода в новый статус, по которым мы получим время.
Остались те заявки, которые до не завершились в установленный промежуток времени (не сменили статус)
По этим заявкам просматриваем ВСЮ историю с начала времен и ищем дату установки статуса. Думаю даже такое решение уже удовлетворяет выдвинутым критериям.
Т.е. достаточно одного регистра остатков.
А если так уж хочется ускорить определение текущего статуса, для этого можно так уж и быть завести периодический регистр сведений.
Тогда срез последних на заданную дату окончания периода даст ответ на вопрос, когда был установлен статус. (И опять таки обошлись без оборотного регистра)
68. Sergey.Noskov 1433 02.09.16 13:49 Сейчас в теме
(67) Ovrfox, идеологически это не верное решение т.к. обращаться в отчетах к документам - мувитон. Задачу остатков тож можно решить на документах, нет никаких проблем написать такой запрос.
Второй момент, использую таблицу агрегата, рассчитанную на каждый день, вы получите по статусу одну запись в сутки, т.е. объем читаемых данных будет не сравнимо меньшим.

З.Ы. пока вам отвечал, понял, что в оборотном регистре не хватает полей..
speshuric; +1 Ответить
167. sss999 50 08.09.22 23:29 Сейчас в теме
(68) можете обьяснить почему плохо обращаться к документам в отчетах, кроме того что это "плохо"?
69. Sergey.Noskov 1433 02.09.16 13:58 Сейчас в теме
(66) корректировка решения 2 в том виде не считает среднее, необходимо в оборотный регистр добавить ресурс Количество, которое всегда заполняем единичкой и запрос получения среднего будет такой:

ВЫБРАТЬ СРЕДНЕЕ(Т3.ВремяОбработки) КАК СреднееВремяОбработки
ИЗ(
ВЫБРАТЬ 
    (Т1.ВремяОбработкиОборот - &КонецТекДняМинусДатаКон)/Т1.КоличествоОборот КАК ВремяОбработки 
ИЗ РегистрНакопления.СреднееВремяОбработкиЗаказа.Обороты(, &ДатаКон,, Статус = &Новый) КАК Т1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 
    Т2.ВремяОбработкиОборот/Т2.КоличествоОборот 
ИЗ РегистрНакопления.СреднееВремяОбработкиЗаказа.Обороты(&ДатаНач, &ДатаКон, , Статус = &ВРаботе) КАК Т2) КАК Т3
Показать
75. Ovrfox 14 02.09.16 17:07 Сейчас в теме
(69) (67) Можно просто добавить реквизит (не измерение) в регистр остатков и заполнять его, если не нравится в документе.
В том то и дело, что таком варинате вы получаете только одну запись на весь период, а не на каждый день, и такой вариант значительно менее нагружает систему.
А за необходимость регламентных заданий для заполнения я даже не считаю.
78. Sergey.Noskov 1433 02.09.16 17:23 Сейчас в теме
(75) Ovrfox,
В том то и дело, что таком варинате вы получаете только одну запись на весь период, а не на каждый день, и такой вариант значительно менее нагружает систему.

Это в случае, получения остатка одна запись? Например остаток на последнюю дату месяца? Плюс "анализ движений между датами" (ваши слова) и там тож не одна запись.
80. Ovrfox 14 02.09.16 17:46 Сейчас в теме
(78) Вы не правы, именно анализ движений между датами вернет одну запись. Проанализировать движения нужно будет по фильтру полученных по остаткам заказов
В варианте с регистром оборотов (если он даже рассчитывается ежедневно, что нонсенс) вам нужно отобрать за тот же период обороты, которые вам вернут не одну, столько записей, сколько дней длился переход между периодами.
И на всякий случая - я знаю что такое группировка, но это не означает, что записи не будут прочитаны.

Выигрыш во времени, возможно будет при условии, что все заказы выполняются более месяца и стандартные периоды запросов по пару лет. Тогда за счет предрассчитаннных итогов обороты будут быстрее.
И то не уверен.
82. Sergey.Noskov 1433 02.09.16 17:56 Сейчас в теме
(80) Ovrfox,
именно анализ движений между датами вернет одну запись

возможно сходу не вижу красоту задумки, можно пример запроса?
84. Ovrfox 14 02.09.16 18:12 Сейчас в теме
(82) Обычная выборка движений по регистру остатков
У нас уже известны заказы, зачит что типа
Выбор Заказ, регистратор, кводней(атрибут) из РегистрОстатков.НашРегистр где Период между &НачДат и &КонДат и заказ в (&мЗаказов) и статус = &КонСтатус
87. Sergey.Noskov 1433 02.09.16 18:35 Сейчас в теме
(84) Ovrfox, так там не будет "заказ в (&мЗаказов)", только период и статус... и для одного статуса в движениях будет явно не одна строка. В агрегах же, по задумке разработчиков, хранится свернутая информация в тех разрезах, которые мы сами указываем.
91. Ovrfox 14 05.09.16 09:17 Сейчас в теме
(87) цитирую
У нас уже известны заказы, зачит что типа
Выбор Заказ, регистратор, кводней(атрибут) из РегистрОстатков.НашРегистр где Период между &НачДат и &КонДат и заказ в (&мЗаказов) и статус = &КонСтатус

Известны заказы, потому что перед запросом движений мы уже получили остатки.
Будет ТОЛЬКО одна запись я предполагаю из того, что Один статус назначается только Один раз.
ОДНА запись для каждого ЗАКАЗА, а не всего.
В противоположность ОДНА запись для каждого ДНЯ действия Статуса.
92. Sergey.Noskov 1433 05.09.16 13:34 Сейчас в теме
(91) Ovrfox, Сколько будет заявок в статусе ВРаботе? Если совсем грубо прикинуть, то 8640*80%(это только те, что берут в работу в течении дня)=6912, период отчета обычно 5 дней т.е. 34560 движений. Цитирую вас:
В противоположность ОДНА запись для каждого ДНЯ действия Статуса.
Что бы агрегат для статуса "Новый" накопил такое число строк, надо что б заявки зависали на 94 года... Напомню, что это решение было вне конкурса т.к. имеет очевидные недостатки, но не те, за которые цепляетесь вы.
Вы бы оформили свое решение отдельно, с описанием архитектуры и примерами запросов, а то по кусочкам приходится восстанавливать картинку и пытаться понять как и что планируется считать.
96. Ovrfox 14 05.09.16 18:37 Сейчас в теме
(92) Для одной заявки надо просмотреть историю одной заявки, для которой ВНЕ ЗАВИСИМОСТИ от срока ее выполнения одна запись в промежутке запроса (или ни одной, тогда она еще не завершена)
В вашем предложении, насколько я понял, к-во записей для 1 заказа равно к-ву дней его перехода, в худшем случае это равно промежутку запроса , в лучшем 1 запись.
Умножим теперь это на к-во заявок и получим искомое. А именно, каждый день только 20 % заявок меняют свой статус, в остальных, 80 % случаев будет сделана повторная запись .
Это слишком много записей. Попробуйте оценить свое решение по к-ву записей в регистр. (во все регистры решения)
97. Sergey.Noskov 1433 05.09.16 22:11 Сейчас в теме
(96) Ovrfox, кхммм...
в агрегате измерение Заявка отключаем:
Дополнительно создаем агрегат с периодом День и аналитикой Статус.
так сколько будет строк? Одна строка в сутки будет, не для каждого заказа одна строка, а реально одна строка.
P.S. а я все в толк не возьму о чем вообще тут можно спорить.
98. Ovrfox 14 06.09.16 09:40 Сейчас в теме
(97) цитирую ваше решение
Для покрытия требования 2.1. есть риск свалиться в написание многоэтажных запросов, на деле достаточно организовать запись в регистр следующим образом: при записи новой заявки время обработки рассчитываем на конец текущего дня и каждую ночь увеличиваем его на сутки.

Т.е. как я понимаю для КАЖДОЙ заявки вы КАЖДУЮ ночь увеличиваете время на сутки. Я так понял, что с помощью записи оборотов в соотвествующий регистр.
Но после ваших слов засомневался. Вы что, собирались увеличивать исходную запись? А как в этом случае отработать перепроведение документа, если потребуется? А как узнать - отработало ли регламентное задание? Или вдруг оно отработало дважды?
Поясните подробнее свое решение, какая именно структура оборотного регистра и какие именно записи вы собирались записывать при смене статуса и в регламентном задании.
А также ответьте на вопрос регламентное задание должно записать новые записи или откорректировать уже записанное ранее?
99. Sergey.Noskov 1433 06.09.16 12:08 Сейчас в теме
(98) Ovrfox
Время обработки корректируем изменяя исходную запись.
С перепроведением и прочим не вижу проблем, что то вроде
Движение.ВремяОбработки = КонецДня(ТекущаяДата()) - Движение.Период;
соответственно двойное выполнение не страшно. Отработало ли регламентное - совсем другая задача, достаточно легко решаемая.
86. Ovrfox 14 02.09.16 18:23 Сейчас в теме
(82) Ovrfox, Извините, в запросе забыл указать условие по типу движения - только приход.
Для отправки сообщения требуется регистрация/авторизация