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

13.01.17

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

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

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

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

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


Содержание:

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

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

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

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

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

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

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

Выводы.

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

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

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

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

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

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

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

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

- запись с замещением и признаком обмена данными

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

- запись без замещения с признаком обмена данных

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

Промежуточные выводы. Сравниваем 8.2 и 8.3, независимые и подчиненные регистры. 

Регистры сведений с итогами.

- запись с замещением ("по умолчанию")

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

подчиненные регистры с таблицей итогов

Выводы по регистрам с итогами

3. Как работает СрезПоследних (СрезПервых) в запросе

срез последних на языке 1С для регистра с периодичностью <> по позиции регистратора

срез последних на языке 1С для регистра с периодичность "По позиции регистратора"

самая частая ошибка при фильтрации среза

отличия 8.2 и 8.3 для запросов среза

когда используется таблица итогов

Выводы

Заключение


Все примеры будут даны для версий 1С 8.3.6 - 8.3.8. На 8.2.19 логика на 99% совпадает с работой 8.3, есть несколько отличий, про которые обязательно упомяну.

Работа платформы с регистрами сведений несколько раз менялась. 8.3 стала более оптимальной и как бы мне не хотелось "потыкать палочкой" в 8.1 и в ранние версии 8.2, дабы показать как все было плохо, решил этого не делать. Кому станет любопытно "как было" - получит мотивацию изучить вопрос.

Вводная.

Основное назначение регистров сведений - хранить информацию НЕ ссылочного типа в разрезе комбинации измерений и, как частный случай, хранить изменяемые во времени значения. По большому счету, в регистре можно хранить что угодно, но получить ссылку на саму запись регистра и сохранить её (ссылку на запись) в реквизитах другого объекта, например справочника, уже не получится. Собственно отличие ссылочных типов от не ссылочных - это один из краеугольных камней 1С т.ч. кто прогуливал - учим мат часть.

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

Спустимся с уровня абстракции "конфигуратор" на уровень "СУБД" и вспомним, в очередной раз, что все данные базы хранятся именно в СУБД. Это может быть файловая база (тут в роли СУБД платформа 1С), PosgreSQL, IBM DB2, Oracle и MS SQL Server.

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

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

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

До версии 8.2.15 непериодический регистр сведений не имел кластерного индекса, начиная с этой версии, платформа создает кластерный индекс, состоящий из измерений (в том порядке, как они заданы в конфигураторе).

Версия 8.3 принесла еще несколько важных изменений:

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

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

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

Индекс Условие и описание Кластерный
Измерение1 + [Измерение2 +...] Есть хоть одно измерение регистра. Индекс, включающий все измерения в том порядке, в котором они заданы при конфигурировании. начиная с 8.2.15 и 8.3
ИзмерениеN + Измерение1 + [Измерение2 +...] Измерению "ИзмерениеN" задано свойство "Индексировать" или свойство "Ведущее" и при этом это не первое и не единственное измерение. Первое поле - ИзмерениеN, затем все остальные измерения в том порядке, в котором они заданы при конфигурировании. -
Реквизит + Измерение1 + [Измерение2 +...] Реквизиту "Реквизит" задано свойство "Индексировать". Индекс, в котором первое поле - Реквизит, затем все измерения в том порядке, в котором они заданы при конфигурировании. -
Ресурс + Измерение1 + [Измерение2 +...] Ресурсу "Ресурс" задано свойство "Индексировать". Индекс, в котором первое поле - Ресурс, затем все измерения в том порядке, в котором они заданы при конфигурировании. -
SimpleKey (удалено в 8.3.3 и старше) Количество измерений больше одного. Используется для обхода регистра при реструктуризации, а также для выборки записей с использованием оптимального порядка обхода. -

Непериодический, подчиненный регистр

Индекс Условие и описание Кластерный
Регистратор + НомерСтроки Создается всегда. Да, всегда
Измерение1 + [Измерение2 +...] Есть хоть одно измерение регистра. -
ИзмерениеN + Измерение1 + [Измерение2 +...] Измерению "ИзмерениеN" задано свойство "Индексировать" или свойство "Ведущее" и при этом это не первое и не единственное измерение -
Реквизит + Измерение1 + [Измерение2 +...] Реквизиту "Реквизит" задано свойство "Индексировать". -
Ресурс + Измерение1 + [Измерение2 +...] Ресурсу "Ресурс" задано свойство "Индексировать". -
SimpleKey (удалено в 8.3.3 и старше) Количество измерений больше одного. Используется для обхода регистра при реструктуризации, а также для выборки записей с использованием оптимального порядка обхода. -

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

Индекс Условие и описание Кластерный
Период + [Измерение1 + ...] Создается всегда. для 8.2
Измерение1 + [Измерение2 +...] + Период Есть хоть одно измерение регистра. для 8.3
ИзмерениеN + Период + Измерение1 + [Измерение2 +...] Измерению "ИзмерениеN" задано свойство "Индексировать" или свойство "Ведущее" и при этом это не единственное измерение. -
Реквизит + Период + [Измерение1 + ...] Реквизиту "Реквизит" задано свойство "Индексировать". -
Ресурс + Период + [Измерение1 + ...] Ресурсу "Ресурс" задано свойство "Индексировать". -

Периодический, подчиненный регистр

Индекс Условие и описание Кластерный
Регистратор + НомерСтроки Создается всегда. -
Период + [Измерение1 + ...] + [Активность]* Создается всегда. для 8.2
Измерение1 + [Измерение2 +...] + Период + [Активность]* Есть хоть одно измерение регистра. для 8.3
ИзмерениеN + Период + Измерение1 + [Измерение2 +...] + [Активность]*

Измерению "ИзмерениеN" задано свойство "Индексировать" или свойство "Ведущее" и при этом это не единственное измерение.

-
Реквизит + Период + [Измерение1 + ...] + [Активность]* Реквизиту "Реквизит" задано свойство "Индексировать". -
Ресурс + Период + [Измерение1 + ...] Ресурсу "Ресурс" задано свойство "Индексировать". -

* только для 8.3

Периодический, с периодичностью "По позиции регистратора"

Индекс Условие и описание Кластерный
Период + Регистратор + НомерСтроки Создается всегда. для 8.2
Регистратор + НомерСтроки Создается всегда. -
Измерение1 + [Измерение2 + ...] + Период + Регистратор + НомерСтроки + [Активность]* Есть хоть одно измерение регистра.  для 8.3
Измерение + Период + Регистратор + НомерСтроки + [Активность]* Измерению "Измерение" задано свойство "Индексировать". -
Реквизит + Период + Регистратор + НомерСтроки + [Активность]* Реквизиту "Реквизит" задано свойство "Индексировать". -
Ресурс + Период + Регистратор + НомерСтроки Ресурсу "Ресурс" задано свойство "Индексировать". -

* только для 8.3 

Индексы таблицы итогов среза первых и среза последних. Только для 8.3.

Индекс Условие и описание Кластерный
Измерение1 + [Измерение2 + ...] Есть хоть одно измерение регистра. -
Реквизит + [Измерение1 + ...] Реквизиту "Реквизит" задано свойство "Индексировать" -
Ресурс + [Измерение1 + ...] Ресурсу "Ресурс" задано свойство "Индексировать" -

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

Можно сказать, что в 8.3 структура индексов продумана лучше - в индексы добавлена "Активность", использование в качестве кластерных индексов вида "Измерение1 + [Измерение2 +...] + Период" снижают ресурсоемкость запросов (это ни в коем разе не панацея, но ниже рассмотрим примеры запросов и еще раз затронем тему эффективности индексов). При этом к 8.3 есть и вопросы, например не понятно, почему при индексировании ресурса в периодическом регистре в индекс не добавляется "Активность" и почему нет кластерного индекса в таблицах срезов.

Выводы:

  1. Порядок измерений, заданный в конфигураторе, влияет на структуру индексов и, как следствие, может влиять на скорость выборки данных. Дело в том, что индекс "не работает" если нет фильтра по первому его полю, при этом поля, по которым накладываются фильтры, должны идти в индексе друг за другом.
    Например, есть непериодический, независимый регистр со следующими измерениями: Организация, Склад, Номенклатура. В СУБД получаем таблицу с одним индексом Организация + Склад + Номенклатура. Теперь рассмотрим различные варианты запросов к этому регистру:
    • запрос с условием отбора только по Номенклатуре ...ГДЕ Номенклатура = &Номенклатура - индекс "работать" не будет.
    • запрос с фильтром ...ГДЕ Склад = &Склад И Номенклатура = &Номенклатура - индекс так же не будет "работать" т.к. и в том и в другом случае нет отбора по Организации.
    • запрос с отбором ...ГДЕ Организация = &Организация И Номенклатура = &Номенклатура - индекс будет работать, но не эффективно - все данные по Организации будут получены быстро, но потом все они будут просканированы (построчно просмотрены), проверяя та ли Номенклатура в этой строке. А т.к. организаций обычно всего одна, а Номенклатур очень много, то прочитаны будут все строки регистра. 

    Для этого набора запросов самым оптимальным будет поменять местами измерения Организация и Номенклатура (получим индекс Номенклатура + Склад + Организация), тогда вопрос с эффективным использованием индекса будет только для запроса ...ГДЕ Организация = &Организация И Номенклатура = &Номенклатура. Если в базе только одна организация, то индекс можно назвать подходящим т.к. условие по номенклатуре уже вернет малое число строк, которые очень быстро проверятся на соответствие условию по организации. Если организаций много, то правильным решением будет дополнительно создать индекс Организация + Номенклатура [+ Склад].

    Итоговый универсальный вариант: измерения в порядке следования Номенклатура, Склад, Организация, у измерения Организация ставим свойство Индексировать.


    Из этого примера можно сделать очень интересный вывод - спроектировать оптимальную архитектуру регистра можно только при известном пуле запросов, которые будут к нему обращаться, и понимании как будут распределяться данные!

  2. Стремитесь к тому, что бы кластерный индекс регистра покрывал потребности самых частых запросов. Кластерный индекс содержит в себе все данные таблицы, для простоты понимания можно сказать, что кластерный индекс это надстройка над данными. Некластерный содержит только значения тех полей, из которых он состоит. Например, есть периодический независимый регистр сведений "ЦеныТоваров" с измерениями Товар + ВидЦены и ресурсом Цена. Самая частая задача - зная товар и вид цены получить актуальную цену товара, т.е. по значениям измерений получить значение ресурса "Цена". Для этой цели идеально подходит типовой индекс "Товар + ВидЦены + Период". В 8.3 этот индекс будет кластерным и значение цены будет получено сразу. В 8.2 этот индекс некластерный, что означает необходимость дополнительно "залезть" в таблицу данных и получить нужное значение.
  3. Не желательно создавать регистры с большим числом измерений. Обратная сторона индексов - накладные расходы на их содержание. Чем "легче" индекс, т.е. чем меньше полей в него входит и чем меньше места занимают данные в этих полях, тем быстрее происходит запись и поиск данных. Это не стоит расценивать как табу, задачи бывают разные, но если вы проектируете регистр, для которого важна быстрая вставка больших объемов данных, то старайтесь снизить и число измерений и число индексов в этой таблице.
    Особо отмечу, что индекс не может содержать более 16 полей и периодический регистр с 16ю измерениями платформа усечет поля в индексах - создаст без последнего измерения: [Период + Измерение1 + ... + Измерение15], а второй индекс не будет содержать поле Период: [Измерение1 + ... + Измерение16]. Работа с таким регистром может вызвать проблемы со скоростью получения данных, хотя с прикладной точки зрения ошибок не будет. Стоит учитывать составные типы, одно измерение составного типа в СУБД будет состоять из нескольких полей (в самом жестком случае - до 7 полей на одно измерение).

 Знания что такое индекс, как он работает, чем отличается кластерный от не кластерного - очень важны, кто не уверен в своих силах, могу посоветовать доклады Дмитрия Короткевича.

#Содержание

2. Запись регистра.

Казалось бы, простая запись регистра, в чем тут разбираться? Но у набора записей существует три режима записи, да еще и менеджер записи добавляется т.ч. "будем посмотреть".

Вводные: база на 8.3 (проверено на 8.3.6 - 8.3.8), несколько регистров сведений и MS SQL Server. Разбираться, что происходит в СУБД, будем путем трассировок запросов (Profiler).

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

Структура регистра:

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

Выполняемый код 1С:

ОбъектРегистр = РегистрыСведений.НепериодическийНезависимыйРС.СоздатьНаборЗаписей(); 

ОбъектРегистр.ОбменДанными.Загрузка = РежимЗагрузки; 

СтруктураОтбора = Новый Структура(); 
СтруктураОтбора.Вставить("Измерение1", Новый УникальныйИдентификатор); 
СтруктураОтбора.Вставить("Измерение2", Новый УникальныйИдентификатор); 
СтруктураОтбора.Вставить("Измерение3", Новый УникальныйИдентификатор); 

ОбъектРегистр.Отбор.Измерение1.Установить(СтруктураОтбора.Измерение1); 
ОбъектРегистр.Отбор.Измерение2.Установить(СтруктураОтбора.Измерение2); 
ОбъектРегистр.Отбор.Измерение3.Установить(СтруктураОтбора.Измерение3); 

ЗаполнитьЗначенияСвойств(ОбъектРегистр.Добавить(), СтруктураОтбора); 

ОбъектРегистр.Записать(Замещать);

Независимые РС. Режим 1. Запись "по умолчанию".

// (!)Режим загрузки при замещении не влияет на генерируемые платформой запросы 
ОбъектРегистр.ОбменДанными.Загрузка = <Любой>; 
// ... 
ОбъектРегистр.Записать(Истина);

Генерируемые запросы:

1.1. Открываем транзакцию

BEGIN TRANSACTION

1.2. Удаление записей по значению установленного отбора записываемого набора. Обращаю внимание, что этот запрос выполняется всегда.

DELETE FROM T1 FROM dbo._InfoRg288 T1 WHERE T1._Fld289 = @P1 AND T1._Fld290 = @P2 AND T1._Fld291 = @P3

если установить отбор только на Измерение1 и Измерение2, то условие сократиться до  <<...WHERE T1._Fld289 = @P1 AND T1._Fld290 = @P2 >>, если отборы не установлены - удаляться все записи.

1.3. Вставляем запись в таблицу.

INSERT INTO dbo._InfoRg288(_Fld289,_Fld290,_Fld291,_Fld292,_Fld293) VALUES(@P1,@P2,@P3,@P4,@P5)

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

1.4. Фиксируем транзакцию

COMMIT TRANSACTION

Как видим - все достаточно просто, сначала удаляем записи, затем вставляем. Кстати, в 8.2 поведение аналогичное. В чем минус? Только в том, что удаление происходит всегда, даже если вы точно знаете, что вставляемые записи являются новыми.

#Содержание

Независимые РС. Режим 2. Запись без замещения.

 ОбъектРегистр.ОбменДанными.Загрузка = ЛОЖЬ;
 // ...
 ОбъектРегистр.Записать(ЛОЖЬ);

Генерируемые запросы:

2.1. Открываем транзакцию

BEGIN TRANSACTION

2.2. Создаем временную таблицу той же структуры, что и регистр.

CREATE TABLE #tt1(_Fld289 NVARCHAR(10), _Fld290 NVARCHAR(10), _Fld291 NVARCHAR(10), _Fld292 NVARCHAR(10), _Fld293 NVARCHAR(10))

временная таблица однотипной структуры создается только однажды в рамках одного и того же соединения с СУБД. Проще говоря, если пользователь записывает тот же регистр второй и более раз, то создание временной таблицы у него будет только при первой записи

2.3. Вставляем строку регистра во временную таблицу.

INSERT INTO #tt1(_Fld289,_Fld290,_Fld291,_Fld292,_Fld293) VALUES(@P1,@P2,@P3,@P4,@P5

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

2.4. Проверяем, есть ли в таблице записи с таким же набором измерений 

SELECT

T1._Fld289
T1
._Fld290
T1
._Fld291
T1
._Fld292
T1
._Fld293

FROM #tt1 T1 WITH(NOLOCK)

INNER JOIN dbo._InfoRg288 T2

 ON T1._Fld289 = T2._Fld289 AND T1._Fld290 = T2._Fld290 AND T1._Fld291 = T2._Fld291

если запрос не пустой - выдаем ошибку. Для периодического регистра в условия соединения таблиц добавляется условие по полю _Period.

2.5. Вставляем данные из временной таблицы в таблицу регистра

INSERT INTO dbo._InfoRg288(_Fld289, _Fld290, _Fld291, _Fld292, _Fld293) SELECT

T1._Fld289
T1
._Fld290
T1
._Fld291
T1
._Fld292
T1
._Fld293

FROM #tt1 T1 WITH(NOLOCK)

2.6. Фиксируем транзакцию

COMMIT TRANSACTION

2.7. Очищаем временную таблицу. Обращаю внимание, что вспомогательную таблицу 8.3 очищает вне транзакции, что логично.

TRUNCATE TABLE #tt1

Сразу скажу про отличия с 8.2 - запросы пунктов 2.6 и 2.7 поменяны местами, т.е. в 8.2 сначала очищаем временную таблицу и только затем фиксируем транзакцию. Повторюсь, поведение 8.3 выглядит более логичным. Так же 8.2 дополнительно, после пункта 2.2, выполняет еще один запрос примерно такого содержания:

INSERT INTO #tt1 SELECT * FROM _InfoRg288 T1 WHERE 1=0

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

Итак, очевидно, что запись без замещения содержит больше запросов и будет проигрывать записи с замещением. Тут нет подводных камней, просто  используйте режимы по прямому назначению, если при записи набора надо к уже существующим данным добавить новые строки, то Записать(ЛОЖЬ), если записать новый или перезаписать целиком существующий набор записей, то Записать().

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

#Содержание

Независимые РС. Режим 3. Запись без проверок.

 ОбъектРегистр.ОбменДанными.Загрузка = ИСТИНА;
 // ...
 ОбъектРегистр.Записать(ЛОЖЬ);

Генерируемые запросы:

3.1. Открываем транзакцию

BEGIN TRANSACTION

3.2. Вставляем запись в таблицу.

INSERT INTO dbo._InfoRg288(_Fld289,_Fld290,_Fld291,_Fld292,_Fld293) VALUES(@P1,@P2,@P3,@P4,@P5)

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

3.3. Фиксируем транзакцию

COMMIT TRANSACTION

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

Ошибка неуникальности

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

#Содержание

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

Да, чуть не забыл про менеджер записи регистра сведений. Первое отличие - у менеджера нет свойства ОбменДанными, следовательно, записать в режиме загрузки с его использованием не выйдет. Отличия в запросах СУБД тут появляются только при определенных обстоятельствах. Для случая простой записи данных, например:

 МенеджерЗаписи = РегистрыСведений.НепериодическийНезависимыйРС.СоздатьМенеджерЗаписи();

 МенеджерЗаписи.Измерение1 = Новый УникальныйИдентификатор;
 МенеджерЗаписи.Измерение2 = Новый УникальныйИдентификатор;
 МенеджерЗаписи.Измерение3 = Новый УникальныйИдентификатор;

 МенеджерЗаписи.Записать(Замещать);

поведение платформы будет ровно такое же. Оно изменится в следующем случае:

 МенеджерЗаписи = РегистрыСведений.НепериодическийНезависимыйРС.СоздатьМенеджерЗаписи();
 МенеджерЗаписи.Измерение1 = "Измерение1";
 МенеджерЗаписи.Измерение2 = "Измерение2";
 МенеджерЗаписи.Измерение3 = "Измерение3";
 МенеджерЗаписи.Прочитать();

 Если МенеджерЗаписи.Выбран() Тогда 
	 // тогда будет отличаться от записи набора записей
 Иначе 
	 // записи не существует, а значит
	 // ранее установленные значения измерений "сбросились"
	 // поэтому необходимо установить их заново
	 МенеджерЗаписи.Измерение1 = "Измерение1";
	 МенеджерЗаписи.Измерение2 = "Измерение2";
 КонецЕсли;
 
 // меняем значение измерения
 МенеджерЗаписи.Измерение3 = "Измерение4";
 МенеджерЗаписи.Записать(Замещать);

и при условии, что прочитанные данные существуют. Фактически мы изменяем существующее значение измерения в записи. При этом платформа добавляет запрос (сразу после начала транзакции) на удаление старых данных:

DELETE FROM T1 FROM dbo._InfoRg288T1 WHERE T1._Fld289='Измерение1' AND T1._Fld290='Измерение2' AND T1._Fld291='Измерение3'

Обращаю внимание еще на одну особенность, при чтении, если значение какого либо измерения не указано, то отбор устанавливается по пустому значению этого типа данных:

 МенеджерЗаписи = РегистрыСведений.НепериодическийНезависимыйРС.СоздатьМенеджерЗаписи();
 МенеджерЗаписи.Измерение1 = "Измерение1";
 МенеджерЗаписи.Измерение2 = "Измерение2";
 // Измерение3 не указываем
 МенеджерЗаписи.Прочитать();

выполняется примерно следующий запрос:

SELECT FROM dbo._InfoRg288T1 WHERE T1._Fld289='Измерение1' AND T1._Fld290='Измерение2' AND T1._Fld291=''

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

Для себя определился только с одной областью применения - изменять значение измерения у конкретной записи. Подытожу цитатой с ИТС:

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

#Содержание

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

Структура регистра:

Исполняемый код:

 ОбъектРегистр = РегистрыСведений.НепериодическийПодчиненныйРС.СоздатьНаборЗаписей(); 
 ОбъектРегистр.ОбменДанными.Загрузка = РежимЗагрузки; 

 Регистратор = Документы.Тест.ПолучитьСсылку(Новый УникальныйИдентификатор); 
 ОбъектРегистр.Отбор.Регистратор.Установить(Регистратор); 
 
 Запись             = ОбъектРегистр.Добавить(); 
 Запись.Регистратор = Регистратор; 
 Запись.Период      = ТекущаяДата(); 
 Запись.Измерение1 = Новый УникальныйИдентификатор; 
 Запись.Измерение2 = Новый УникальныйИдентификатор; 
 Запись.Измерение3 = Новый УникальныйИдентификатор; 
 
 ОбъектРегистр.Записать(Замещать); 

Подчиненные РС. Режим 1. Запись "по умолчанию".

 ОбъектРегистр.ОбменДанными.Загрузка = ЛОЖЬ;
 // ...
 ОбъектРегистр.Записать(ИСТИНА);

Генерируемые запросы:

1.1. Открываем транзакцию

BEGIN TRANSACTION

1.2. Читаем 100 000 плюс одну запись регистра

SELECT TOP 100001

T1._Active,

T1._Fld302,

T1._Fld303,

T1._Fld304

FROM dbo._InfoRg301 T1

WHERE T1._RecorderRRef = @P1

запрос выполняется только для управляемого режима блокировок. Судя по всему, запрос используется для установки управляемых блокировок по значению измерений и определения надо ли делать эскалацию блокировок. В 8.2 запрос меняется на "SELECT TOP 20001..." и добавляется явно ненужная сортировка по номеру строки "ORDER BY T1._LineNo DESC".

1.3. Удаляем все записи по регистратору

DELETE FROM T1 FROM dbo._InfoRg301 T1 WHERE T1._RecorderRRef = @P1

1.4. Создаем временную таблицу

CREATE TABLE #tt1(

_RecorderRRef BINARY(16),

_LineNo NUMERIC(9, 0),

_Active BINARY(1),

_Fld302 NVARCHAR(10),

_Fld303 NVARCHAR(10),

_Fld304 NVARCHAR(10),

_Fld305 NVARCHAR(10),

_Fld306 NVARCHAR(10))

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

1.5. Заполняем временную таблицу

INSERT INTO #tt1(_RecorderRRef,_LineNo,_Active,_Fld302,_Fld303,_Fld304,_Fld305,_Fld306) VALUES(@P1,@P2,@P3,@P4,@P5,@P6,@P7,@P8)

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

1.6. Проверка на уникальность по составу измерений.

SELECT

       T1._RecorderRRef,

       T1._LineNo,

       T1._Active,

       T1._Fld302,

       T1._Fld303,

       T1._Fld304,

       T1._Fld305,

       T1._Fld306

FROM #tt1 T1 WITH(NOLOCK)

INNER JOIN dbo._InfoRg301 T2

ON T1._Fld302 = T2._Fld302 AND T1._Fld303 = T2._Fld303 AND T1._Fld304 = T2._Fld304

для периодического регистра в условия соединения добавится соединение по полю _Period

1.7. Вставляем записи в таблицу регистра из вспомогательной таблицы

INSERT INTO dbo._InfoRg301(_RecorderRRef, _LineNo, _Active, _Fld302, _Fld303, _Fld304, _Fld305, _Fld306) SELECT

       T1._RecorderRRef,

       T1._LineNo,

       T1._Active,

       T1._Fld302,

       T1._Fld303,

       T1._Fld304,

       T1._Fld305,

       T1._Fld306

FROM #tt1 T1 WITH(NOLOCK)

1.8. Завершение транзакции

COMMIT TRANSACTION

1.9. Очищаем вспомогательную временную таблицу

TRUNCATE TABLE #tt1

#Содержание

Подчиненные РС. Режим 2. Запись с замещением и признаком обмена данными.

 ОбъектРегистр.ОбменДанными.Загрузка = ИСТИНА;
 // ...
 ОбъектРегистр.Записать(ИСТИНА);

Генерируемые запросы:

2.1. Открываем транзакцию

BEGIN TRANSACTION

2.2. Для установки управляемой блокировки удаляемых данных читаем 100 000 плюс одну запись регистра (подробно в п. 1.2)

SELECT TOP 100001

       T1._Active,

       T1._Fld302,

       T1._Fld303,

       T1._Fld304

FROM dbo._InfoRg301 T1

WHERE T1._RecorderRRef = @P1

2.3. Удаляем все записи по регистратору 

DELETE FROM T1 FROM dbo._InfoRg301 T1 WHERE T1._RecorderRRef = @P1

2.4. Вставляем записи в таблицу регистра 

INSERT INTO dbo._InfoRg301(_RecorderRRef,_LineNo,_Active,_Fld302,_Fld303,_Fld304,_Fld305,_Fld306) VALUES(@P1,@P2,@P3,@P4,@P5,@P6,@P7,@P8)

запросов выполняется для каждой строки записываемого набора движений

2.5. Фиксируем транзакцию 

COMMIT TRANSACTION

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

#Содержание

Подчиненные РС. Режим 3. Запись без замещения.

 ОбъектРегистр.ОбменДанными.Загрузка = ЛОЖЬ;
 // ...
 ОбъектРегистр.Записать(ЛОЖЬ);

Генерируемые запросы:

3.1. Открываем транзакцию

BEGIN TRANSACTION

3.2. Определяем последний существующий номер строки

SELECT TOP 1

       T1._LineNo,

       T1._Active

FROM dbo._InfoRg301 T1

WHERE T1._RecorderRRef = @P1

ORDER BY T1._LineNo DESC

3.3. Читаем 100 000 плюс одну запись регистра

SELECT TOP 100001

       T1._Active,

       T1._Fld302,

       T1._Fld303,

       T1._Fld304

FROM dbo._InfoRg301 T1

WHERE T1._RecorderRRef = @P1

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

3.4. Создаем временную таблицу

CREATE TABLE #tt1(

       _RecorderRRef BINARY(16),

       _LineNo NUMERIC(9, 0),

       _Active BINARY(1),

       _Fld302 NVARCHAR(10),

       _Fld303 NVARCHAR(10),

       _Fld304 NVARCHAR(10),

       _Fld305 NVARCHAR(10),

       _Fld306 NVARCHAR(10))

[этого запроса вы можете не увидеть в трассе т.к. временные таблицы однотипной структуры "кэшируются" в рамках одного и того же соединения с СУБД]

3.5. Заполняем временную таблицу нужными данными

INSERT INTO #tt1(_RecorderRRef,_LineNo,_Active,_Fld302,_Fld303,_Fld304,_Fld305,_Fld306) VALUES(@P1,@P2,@P3,@P4,@P5,@P6,@P7,@P8)

запросов выполняется для каждой строки записываемого набора движений

3.6. Проверка на уникальность

SELECT

       T1._RecorderRRef,

       T1._LineNo,

       T1._Active,

       T1._Fld302,

       T1._Fld303,

       T1._Fld304,

       T1._Fld305,

       T1._Fld306

FROM #tt1 T1 WITH(NOLOCK)

INNER JOIN dbo._InfoRg301 T2

ON T1._Fld302 = T2._Fld302 AND T1._Fld303 = T2._Fld303 AND T1._Fld304 = T2._Fld304

для периодического регистра в условия соединения добавится соединение по полю _Period

3.7. Вставляем записи в таблицу регистра

INSERT INTO dbo._InfoRg301(_RecorderRRef, _LineNo, _Active, _Fld302, _Fld303, _Fld304, _Fld305, _Fld306) SELECT

       T1._RecorderRRef,

       T1._LineNo,

       T1._Active,

       T1._Fld302,

       T1._Fld303,

       T1._Fld304,

       T1._Fld305,

       T1._Fld306

FROM #tt1 T1 WITH(NOLOCK)

3.8. Фиксируем транзакцию

COMMIT TRANSACTION

3.9. Очищаем вспомогательную временную таблицу

TRUNCATE TABLE #tt1

#Содержание


Подчиненные РС. Запись без замещения с признаком обмена данных.

 ОбъектРегистр.ОбменДанными.Загрузка = ИСТИНА;
 // ...
 ОбъектРегистр.Записать(ЛОЖЬ);

вынужден расстроить, так не работает:


Режим 4. Подчиненный РС с периодичностью по позиции регистратора.

Периодические подчиненные регистры рассматривать отдельно смысла нет - совсем чуть-чуть меняются запросы пунктов 1.6 и 3.6 в части соединения двух таблиц - добавляется условие соединения по полю _Period. Но периодичность по позиции регистратора изменяет логику т.к. уникальность набора записей, дэ-факто, определяется и значениями измерений и значением регистратора и делать проверку из 1.6 нет смысла.

Исполняемый код остается тем же, нас интересует режим записи с замещением (запись по умолчанию).

 // (!)Режим загрузки при замещении не влияет на генерируемые платформой запросы
 ОбъектРегистр.ОбменДанными.Загрузка = <ЛЮБОЙ>;
 // ...
 ОбъектРегистр.Записать(ИСТИНА);

Генерируемые запросы:

4.1. Открываем транзакцию

BEGIN TRANSACTION

4.2. Для установки управляемой блокировки удаляемых данных читаем 100 000 плюс одну запись регистра (подробно в п. 1.2)

SELECT TOP 100001

       T1._Active,

       T1._Fld283,

       T1._Fld284,

       T1._Fld285

FROM dbo._InfoRg282 T1

WHERE T1._RecorderRRef = @P1

4.3. Удаляем все записи по регистратору 

DELETE FROM T1 FROM dbo._InfoRg282 T1 WHERE T1._RecorderRRef = @P1

4.4. Вставляем записи в таблицу регистра 

INSERT INTO dbo._InfoRg282(_RecorderRRef,_LineNo,_Active,_Fld283,_Fld284,_Fld285,_Fld286,_Fld287) VALUES(@P1,@P2,@P3,@P4,@P5,@P6,@P7,@P8)

запросов выполняется для каждой строки записываемого набора движений

4.5. Фиксируем транзакцию 

COMMIT TRANSACTION

Запись этого регистра с замещением

 ОбъектРегистр.Записать(ЛОЖЬ);

будет ровно такой же, как в пункте 3. (Подчиненные РС. Режим 3. Запись без замещения.)

Разбираемся в нюансах. Пример: у вас есть документ с табличной частью из 50 000 строк и при проведении вся таб часть помещается в подчиненный регистр сведений. После перехода с 8.2 на 8.3 этот документ будет дольше перепроводиться за счет того, что платформа будет читать все его старые движения (запрос «..SELECT TOP 100001..»). В то же время, перепроведение этого документа в 8.2 хоть и будет быстрее, но вызовет эскалацию управляемой блокировки на весь регистр и никто другой в это время не сможет его (регистр) изменять. В 8.3 эскалации не произойдет и можно параллельно перепроводить другие документы, изменяющие данные регистра. Аналогичное сравнение и с режимом блокировок - в управляемом режиме (на платформе 8.3) документ будет дольше перепроводиться, но будет возможна многопользовательская работа с этим регистром, а в автоматическом режиме блокировок перепроводиться док будет быстрее, но работать сможет только один пользователь из-за эскалаций блокировок уже на уровне СУБД.

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

#Содержание

Тест 3. Регистры сведений с итогами.

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

Для экспериментов возьмем периодический независимый регистр с уже известной структурой и установим свойство "Разрешить итоги: срез последних"

После реструктуризации, в SQL мы получим 3 таблицы:

_InfoRg276 - основная таблица, содержит все записи регистра.

_InfoRgSL296 - таблица итогов, в данном случае срез последних, для среза первых имя таблицы будет содержать "_InfoRgSF...". Очевидно, логика наименования от английских First и Last.

_InfoRgOpt297 - таблица настроек, хранит флаг отключены или нет итоги. Создается своя для каждой таблицы среза.

Исполняемый код:

 ОбъектРегистр = РегистрыСведений.ПериодическийНезависимыйРС.СоздатьНаборЗаписей();

 ОбъектРегистр.ОбменДанными.Загрузка = РежимЗагрузки;

 СтруктураОтбора = Новый Структура();
 СтруктураОтбора.Вставить("Период" , ТекущаяДата());
 СтруктураОтбора.Вставить("Измерение1", Новый УникальныйИдентификатор);
 СтруктураОтбора.Вставить("Измерение2", Новый УникальныйИдентификатор);
 СтруктураОтбора.Вставить("Измерение3", Новый УникальныйИдентификатор);

 ОбъектРегистр.Отбор.Период.Установить(СтруктураОтбора.Период);
 ОбъектРегистр.Отбор.Измерение1.Установить(СтруктураОтбора.Измерение1);
 ОбъектРегистр.Отбор.Измерение2.Установить(СтруктураОтбора.Измерение2);
 ОбъектРегистр.Отбор.Измерение3.Установить(СтруктураОтбора.Измерение3);

 ЗаполнитьЗначенияСвойств(ОбъектРегистр.Добавить(), СтруктураОтбора);

 ОбъектРегистр.Записать(Замещать);

Периодические регистры с таблицей итогов. Режим 1. Запись с замещением.

 // (!)при записи с замещением режим загрузки не влияет на генерируемые платформой запросы
 ОбъектРегистр.ОбменДанными.Загрузка = <ЛЮБОЙ>;
 // ...
 ОбъектРегистр.Записать(ИСТИНА);

Генерируемые запросы:

1.1. Открываем транзакцию 

BEGIN TRANSACTION

1.2. Читаем настройки регистра. 

SELECT T1._SliceUsing FROM dbo._InfoRgOpt297 T1

определяем отключены итоги или нет

1.3. Создаем временную таблицу для сохранения текущих итогов

CREATE TABLE #tt1(

       _Period DATETIME,

       _Fld277 NVARCHAR(10),

       _Fld278 NVARCHAR(10),

       _Fld279 NVARCHAR(10),

       _Fld280 NVARCHAR(10),

       _Fld281 NVARCHAR(10))

[этого запроса вы можете не увидеть в трассе т.к. временные таблицы однотипной структуры "кэшируются" в рамках одного и того же соединения с СУБД]

1.4. Сохраняем данные из таблицы итогов

INSERT INTO #tt1 WITH(TABLOCK)(_Period, _Fld277, _Fld278, _Fld279, _Fld280, _Fld281) SELECT

       T1._Period,

       T1._Fld277,

       T1._Fld278,

       T1._Fld279,

       T1._Fld280,

       T1._Fld281

FROM dbo._InfoRgSL296 T1

INNER JOIN dbo._InfoRg276 T2

ON T2._Fld277 = T1._Fld277 AND T2._Fld278 = T1._Fld278 AND T2._Fld279 = T1._Fld279

WHERE T2._Period = @P1 AND T2._Fld277 = @P2 AND T2._Fld278 = @P3 AND T2._Fld279 = @P4

состав условия WHERE зависит от того, какие отборы установлены в записываемом наборе

1.4а. Если запрос выше не пустой, то выполняется удаление считанных данных

DELETE FROM T1

FROM dbo._InfoRgSL296T1

INNER JOIN dbo._InfoRg276T2

ON T2._Fld277=T1._Fld277 AND T2._Fld278=T1._Fld278 AND T2._Fld279=T1._Fld279

WHERE

(T2._Period=@P1 AND

 T2._Fld277=@P2 AND

 T2._Fld278=@P3 AND

 T2._Fld279=@P4)

AND(T2._Fld277=T1._Fld277 AND

        T2._Fld278=T1._Fld278 AND

        T2._Fld279=T1._Fld279)

состав условия WHERE в первых скобках зависит от того, какие отборы установлены в записываемом наборе

Условие во вторых скобках выглядит избыточным т.к. оно дублирует условие внутреннего соединения таблиц.

1.5. Удаляем старые записи в регистре

DELETE FROM T1 FROM dbo._InfoRg276 T1 WHERE T1._Period = @P1 AND T1._Fld277 = @P2 AND T1._Fld278 = @P3 AND T1._Fld279 = @P4

состав условия WHERE зависит от того, какие отборы установлены в записываемом наборе

1.6. Создаем временную таблицу для записи данных в регистр

CREATE TABLE #tt2(

       _Period DATETIME,

       _Fld277 NVARCHAR(10),

       _Fld278 NVARCHAR(10),

       _Fld279 NVARCHAR(10),

       _Fld280 NVARCHAR(10),

       _Fld281 NVARCHAR(10))

[этого запроса вы можете не увидеть в трассе т.к. временные таблицы однотипной структуры "кэшируются" в рамках одного и того же соединения с СУБД]

1.7. Добавляем данные во вспомогательную таблицу

INSERT INTO #tt2(_Period,_Fld277,_Fld278,_Fld279,_Fld280,_Fld281) VALUES(@P1,@P2,@P3,@P4,@P5,@P6)

таких запросов будет столько, сколько строк в записываемом наборе

1.8. Вставляем данные в регистр из вспомогательной таблицы

INSERT INTO dbo._InfoRg276(_Period, _Fld277, _Fld278, _Fld279, _Fld280, _Fld281) SELECT

       T1._Period,

       T1._Fld277,

       T1._Fld278,

       T1._Fld279,

       T1._Fld280,

       T1._Fld281

FROM #tt2 T1 WITH(NOLOCK)

1.9. Обновляем существующие данные в таблице итогов

UPDATE T6 SET _Period = T1.Period_, _Fld280 = T1.Fld280_, _Fld281 = T1.Fld281_

FROM

       (SELECT

             T5._Period AS Period_,

             T5._Fld277 AS Fld277_,

             T5._Fld278 AS Fld278_,

             T5._Fld279 AS Fld279_,

             T5._Fld280 AS Fld280_,

             T5._Fld281 AS Fld281_

       FROM

             (SELECT

                    T3._Fld277 AS Fld277_,

                    T3._Fld278 AS Fld278_,

                    T3._Fld279 AS Fld279_,

                    MAX(T3._Period) AS MINMAX_PERIOD_

             FROM dbo._InfoRg276 T3

             INNER JOIN #tt2 T4 WITH(NOLOCK)

             ON T3._Fld277 = T4._Fld277 AND T3._Fld278 = T4._Fld278 AND T3._Fld279 = T4._Fld279

             GROUP BY

                    T3._Fld277,

                    T3._Fld278,

                    T3._Fld279) T2

       LEFT OUTER JOIN dbo._InfoRg276 T5

       ON T5._Fld277 = T2.Fld277_ AND T5._Fld278 = T2.Fld278_ AND T5._Fld279 = T2.Fld279_ AND T5._Period = T2.MINMAX_PERIOD_) T1

INNER JOIN dbo._InfoRgSL296 T6

ON T6._Fld277 = T1.Fld277_ AND T6._Fld278 = T1.Fld278_ AND T6._Fld279 = T1.Fld279_

WHERE T6._Period < T1.Period_

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

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

1.10. Вставляем в таблицу среза те строки, по которым ранее не было итогов.

INSERT INTO dbo._InfoRgSL296(_Period, _Fld277, _Fld278, _Fld279, _Fld280, _Fld281) SELECT

T5._Period,

T5._Fld277,

T5._Fld278,

T5._Fld279,

T5._Fld280,

T5._Fld281

FROM

       (SELECT

             T2._Fld277 AS Fld277_,

             T2._Fld278 AS Fld278_,

             T2._Fld279 AS Fld279_,

             MAX(T2._Period) AS MINMAX_PERIOD_

       FROM dbo._InfoRg276 T2

       INNER JOIN #tt2 T3 WITH(NOLOCK)

       ON T2._Fld277 = T3._Fld277 AND T2._Fld278 = T3._Fld278 AND T2._Fld279 = T3._Fld279

       WHERE

             NOT(EXISTS

                           (SELECT

                                  1.0

                           FROM dbo._InfoRgSL296 T4

                           WHERE

                                  T3._Fld277 = T4._Fld277

                                  AND T3._Fld278 = T4._Fld278

                                  AND T3._Fld279 = T4._Fld279))

       GROUP BY

             T2._Fld277,

             T2._Fld278,

             T2._Fld279) T1

LEFT OUTER JOIN dbo._InfoRg276 T5

ON T5._Fld277 = T1.Fld277_ AND T5._Fld278 = T1.Fld278_ AND T5._Fld279 = T1.Fld279_ AND T5._Period = T1.MINMAX_PERIOD_

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

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

1.11. Добавляем не достающие итоги, которые могут появиться при удалении данных.

INSERT INTO dbo._InfoRgSL296(_Period, _Fld277, _Fld278, _Fld279, _Fld280, _Fld281) SELECT

T4._Period,

T4._Fld277,

T4._Fld278,

T4._Fld279,

T4._Fld280,

T4._Fld281

FROM

       (SELECT

             T2._Fld277 AS Fld277_,

             T2._Fld278 AS Fld278_,

             T2._Fld279 AS Fld279_,

             MAX(T2._Period) AS MINMAX_PERIOD_

       FROM dbo._InfoRg276 T2

       INNER JOIN #tt1 T3 WITH(NOLOCK)

       ON T3._Fld277 = T2._Fld277 AND T3._Fld278 = T2._Fld278 AND T3._Fld279 = T2._Fld279

       GROUP BY

             T2._Fld277,

             T2._Fld278,

             T2._Fld279) T1

LEFT OUTER JOIN dbo._InfoRg276 T4

ON T4._Fld277 = T1.Fld277_ AND T4._Fld278 = T1.Fld278_ AND T4._Fld279 = T1.Fld279_ AND T4._Period = T1.MINMAX_PERIOD_

WHERE

       NOT(EXISTS

             (SELECT

                    1.0

             FROM dbo._InfoRgSL296 T5

             WHERE

                    T5._Fld277 = T4._Fld277

                    AND T5._Fld278 = T4._Fld278

                    AND T5._Fld279 = T4._Fld279

                    AND(T5._Period >= T4._Period)))

практический пример - удалили последнюю запись курса валюты. В этом случае актуальная запись среза удалится запросом 1.4а, в тоже время таблица #tt2 будет пуста и предыдущий запрос (п.1.10) итогов не добавит, поэтому выполняем отдельную вставку.

1.12. Очищаем вспомогательную таблицу

TRUNCATE TABLE #tt1

1.13. Фиксируем транзакцию

COMMIT TRANSACTION

1.14. Очищаем вспомогательную таблицу 

TRUNCATE TABLE #tt2

Для таблицы итогов среза первых выполняются запросы, похожие на 1.3, 1.4, 1.4а и 1.9 - 1.12, отличия только в том, что вместо MAX(T..._Period) будет MIN(T..._Period), а в 1.9 и 1.11 инвертируются условия больше-меньше для поля _Period. Приводить их не стану, думаю логика процесса понятна.

#Содержание


Периодические регистры с таблицей итогов. Режим 2. Запись без замещения.

 ОбъектРегистр.Записать(ЛОЖЬ);

В полной трассе нет ничего нового, логика аналогична записи без замещения независимого регистра, плюс добавляются запросы по расчету таблицы итогов из трассы выше, это 1.9 и 1.10.


Подчиненные регистры с таблицей итогов.

При исследовании была найдена ошибка записи подчиненных регистров с итогами. Они просто не записываются, причем не в 8.2, ни в 8.3. Появляются такие ошибки:

Ошибка зарегистрирована в 1С (клик):

Еще одна полезность данного исследования. Статью обещаю дополнить, как выйдет релиз с исправлением.


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

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

#Содержание

3. Как работает СрезПоследних (СрезПервых) в запросе

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

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

Исполняемый код:

Запрос = Новый Запрос; 
Запрос.Текст = 
"ВЫБРАТЬ
|	ПериодическийНезависимыйРССрезПоследних.Измерение1,
|	ПериодическийНезависимыйРССрезПоследних.Измерение2,
|	ПериодическийНезависимыйРССрезПоследних.Измерение3,
|	ПериодическийНезависимыйРССрезПоследних.Ресурс1
|ИЗ
|	РегистрСведений.ПериодическийНезависимыйРС.СрезПоследних(
|			&ТекущаяДата,
|			Измерение1 = ""А""
|				И Измерение2 = ""Б""
|				И Измерение3 = ""В"") КАК ПериодическийНезависимыйРССрезПоследних"; 
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата()); 
Запрос.Выполнить();

и посмотрим какой на самом деле формируется запрос в СУБД:

SELECT

       T1.Fld277_,

       T1.Fld278_,

       T1.Fld279_,

       T1.Fld280_

FROM

       (SELECT

             T4._Fld277 AS Fld277_,

             T4._Fld278 AS Fld278_,

             T4._Fld279 AS Fld279_,

             T4._Fld280 AS Fld280_

       FROM

             (SELECT

                    T3._Fld277 AS Fld277_,

                    T3._Fld278 AS Fld278_,

                    T3._Fld279 AS Fld279_,

                    MAX(T3._Period) AS MAXPERIOD_

             FROM dbo._InfoRg276 T3

             WHERE T3._Period <= @P1 AND((((T3._Fld277 = @P2) AND(T3._Fld278 = @P3)) AND(T3._Fld279 = @P4)))

             GROUP BY

                    T3._Fld277,

                    T3._Fld278,

                    T3._Fld279

             ) T2

       INNER JOIN dbo._InfoRg276 T4

       ON T2.Fld277_ = T4._Fld277

             AND T2.Fld278_ = T4._Fld278

             AND T2.Fld279_ = T4._Fld279

             AND T2.MAXPERIOD_ = T4._Period

       ) T1

Запрос использует таблицу _InfoRg276 - это непосредственно наш регистр сведений ПериодическийНезависимыйРС.  Вопрос "Где наша таблица итогов" оставим на вкусное, а пока давайте разберемся с этой конструкцией т.к. именно она будет использоваться чаще всего. А в 8.2 без вариантов - только так и работает.

Так как в запросе участвует одна таблица - непосредственно наш регистр, то логику запроса СУБД можно воспроизвести в запросе 1С.

Аналог конструкции СрезПоследних() на языке 1С для регистра с периодичность НЕ равной "По позиции регистратора":

ВЫБРАТЬ
   
Т1.Измерение1,
   
Т1.Измерение2,
   
Т1.Измерение3,
   
Т1.Ресурс1
ИЗ
    (ВЫБРАТЬ
       
Т4.Измерение1 КАК Измерение1,
       
Т4.Измерение2 КАК Измерение2,
       
Т4.Измерение3 КАК Измерение3,
       
Т4.Ресурс1 КАК Ресурс1
    ИЗ
        (ВЫБРАТЬ
           
Т3.Измерение1 КАК Измерение1,
           
Т3.Измерение2 КАК Измерение2,
           
Т3.Измерение3 КАК Измерение3,
           
МАКСИМУМ(Т3.Период) КАК MAXPERIOD_
        ИЗ
           
РегистрСведений.ПериодическийНезависимыйРС КАК Т3
        ГДЕ
           
Т3.Период <= &ТекущаяДата
           
И Т3.Измерение1 = "А"
           
И Т3.Измерение2 = "Б"
           
И Т3.Измерение3 = "В"
       
СГРУППИРОВАТЬ ПО
           
Т3.Измерение1,
           
Т3.Измерение2,
           
Т3.Измерение3) КАК Т2
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПериодическийНезависимыйРС КАК Т4
            ПО Т2.Измерение1 = Т4.Измерение1
                И Т2.Измерение2 = Т4.Измерение2
                И Т2.Измерение3 = Т4.Измерение3
                И Т2.MAXPERIOD_ = Т4.Период) КАК Т1

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

#Содержание


Аналог конструкции СрезПоследних() на языке 1С для регистра с периодичность "По позиции регистратора":

ВЫБРАТЬ
   
T1.Измерение1,
   
T1.Измерение2,
   
T1.Измерение3,
   
T1.Ресурс1
ИЗ
    (ВЫБРАТЬ
       
T6.Измерение1 КАК Измерение1,
       
T6.Измерение2 КАК Измерение2,
       
T6.Измерение3 КАК Измерение3,
       
T6.Ресурс1 КАК Ресурс1
    ИЗ
        (ВЫБРАТЬ
           
T3.Измерение1 КАК Измерение1,
           
T3.Измерение2 КАК Измерение2,
           
T3.Измерение3 КАК Измерение3,
           
T3.MAXPERIOD_ КАК MAXPERIOD_,
           
МАКСИМУМ(T5.Регистратор) КАК MAXRECORDERRRef
        ИЗ
            (ВЫБРАТЬ
               
T4.Измерение1 КАК Измерение1,
               
T4.Измерение2 КАК Измерение2,
               
T4.Измерение3 КАК Измерение3,
               
МАКСИМУМ(T4.Период) КАК MAXPERIOD_
            ИЗ
               
РегистрСведений.ПериодическийПоПозицииРегистратораРС КАК T4
            ГДЕ
               
T4.Период <= &ТекущаяДата
               
И T4.Измерение1 = "А"
               
И T4.Измерение2 = "Б"
               
И T4.Измерение3 = "В"

           
СГРУППИРОВАТЬ ПО
               
T4.Измерение1,
               
T4.Измерение2,
               
T4.Измерение3) КАК T3
                ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПериодическийПоПозицииРегистратораРС КАК T5
                ПО T3.Измерение1 = T5.Измерение1
                    И T3.Измерение2 = T5.Измерение2
                    И T3.Измерение3 = T5.Измерение3
                    И T3.MAXPERIOD_ = T5.Период
        ГДЕ
           
T5.Измерение1 = "А"
           
И T5.Измерение2 = "Б"
           
И T5.Измерение3 = "В"
       
СГРУППИРОВАТЬ ПО
           
T3.Измерение1,
           
T3.Измерение2,
           
T3.Измерение3,
           
T3.MAXPERIOD_) КАК T2
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПериодическийПоПозицииРегистратораРС КАК T6
            ПО T2.Измерение1 = T6.Измерение1
                И T2.Измерение2 = T6.Измерение2
                И T2.Измерение3 = T6.Измерение3
                И T2.MAXPERIOD_ = T6.Период
                И T2.MAXRECORDERRRef = T6.Регистратор) КАК T1

Периодичность по позиции регистратора подразумевает, что в одну и ту же секунду может быть несколько записей с одинаковым значением измерений, поэтому одной группировки с расчетом максимального периода не достаточно, необходимо сделать еще одну, вычисляя максимальный регистратор. Запрос имеет уже 3 уровня вложенности и является более сложным для оптимизатора СУБД, как следствие, больше рисков получить не оптимальный план запроса.

#Содержание


Давайте рассмотрим самую частую ошибку при работе с виртуальными таблицами - перенесем фильтры в условие ГДЕ:

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

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

ВЫБРАТЬ
   
Т1.Измерение1,
   
Т1.Измерение2,
   
Т1.Измерение3,
   
Т1.Ресурс1
ИЗ
    (ВЫБРАТЬ
       
Т4.Измерение1 КАК Измерение1,
       
Т4.Измерение2 КАК Измерение2,
       
Т4.Измерение3 КАК Измерение3,
       
Т4.Ресурс1 КАК Ресурс1
    ИЗ
        (ВЫБРАТЬ
           
Т3.Измерение1 КАК Измерение1,
           
Т3.Измерение2 КАК Измерение2,
           
Т3.Измерение3 КАК Измерение3,
           
МАКСИМУМ(Т3.Период) КАК MAXPERIOD_
        ИЗ
           
РегистрСведений.ПериодическийНезависимыйРС КАК Т3
        ГДЕ
           
Т3.Период <= &ТекущаяДата
       
СГРУППИРОВАТЬ ПО
           
Т3.Измерение1,
           
Т3.Измерение2,
           
Т3.Измерение3) КАК Т2
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПериодическийНезависимыйРС КАК Т4
            ПО Т2.Измерение1 = Т4.Измерение1
                И Т2.Измерение2 = Т4.Измерение2
                И Т2.Измерение3 = Т4.Измерение3
                И Т2.MAXPERIOD_ = Т4.Период) КАК Т1
ГДЕ
   
Т1.Измерение1 = "А"
   
И Т1.Измерение2 = "Б"
   
И Т1.Измерение3 = "В"

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

Справедливости ради стоит отметить, что продвинутые СУБД, в частности MS SQL 2012 и 2014 (на 2005 - 2008 не тестил, но думаю поведение аналогичное) в обоих случаях построят одинаковый план запроса и разницу в скорости выполнения вы не заметите. Но файловая база так не умеет, она будет действовать строго по инструкции - написано выбрать все записи, значит надо выбрать все. И нет, это не значит, что если база на SQL, то можно косячить))

#Содержание


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

Сравним два плана однотипного запроса, выполненного в 8.2 и в 8.3 (СУБД: SQL 2014), исполняемый код тот же (разумеется рассматриваем вариант без ошибки). Напомню, что для 8.2 кластерным будет индекс [Период + Измерение1 + Измерение2 + Измерение3], а в 8.3: [Измерение1 + Измерение2 + Измерение3 + Период].

План запроса для 8.2:

тот же запрос для тех же данных но в 8.3:

Почему в 8.2 выполняется больше операций? Если рассмотреть условие по периоду "... Т3.Период <= &ТекущаяДата ...", то становится понятно, что из-за оператора "меньше или равно" эффективно использовать индекс [Период + Измерение1 + Измерение2 + Измерение3] не получится - под это условие могут попасть вообще все записи регистра, каждую из которых потом придется проверить на соответствие фильтрам по измерениям. Поэтому СУБД выбирает индекс  [Измерение1 + Измерение2 + Измерение3 + Период], но т.к. в 8.2 он не кластерный то он не содержит значения ресурсов и реквизитов регистра. Вот за этими значениям и приходится "лезть" в таблицу (читай в кластерный индекс), на плане запросов это операция Key Lookup. Результат - количество прочитанных данных для 8.3 может быть в половину меньше, чем в 8.2.

#Содержание

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

Как уже говорил выше, когда разбирались в отличиях итогов регистра накопления и регистра сведений, таблица среза всегда хранит только одну - самую актуальную запись среза. Поэтому, когда 1С "видит" запрос среза на какую то дату /..СрезПоследних(&ТекущаяДата, ...) /, то она не использует итоги т.к. не может гарантировать, что в них будет находиться актуальный для этой даты срез. Таблица итогов будет использована только если нужен самый-самый актуальный срез, т.е. если в виртуальной таблице не указан параметр периода:

Запрос = Новый Запрос; 
Запрос.Текст = 
"ВЫБРАТЬ
|	ПериодическийНезависимыйРССрезПоследних.Измерение1,
|	ПериодическийНезависимыйРССрезПоследних.Измерение2,
|	ПериодическийНезависимыйРССрезПоследних.Измерение3,
|	ПериодическийНезависимыйРССрезПоследних.Ресурс1
|ИЗ
|	РегистрСведений.ПериодическийНезависимыйРС.СрезПоследних(
|			,
|			Измерение1 = ""А""
|				И Измерение2 = ""Б""
|				И Измерение3 = ""В"") КАК ПериодическийНезависимыйРССрезПоследних"; 
Запрос.Выполнить();

в этом случае в СУБД увидим такой запрос:

SELECT

       T1.Fld277_,

       T1.Fld278_,

       T1.Fld279_,

       T1.Fld280_

FROM

       (SELECT

             T2._Fld277 AS Fld277_,

             T2._Fld278 AS Fld278_,

             T2._Fld279 AS Fld279_,

             T2._Fld280 AS Fld280_

       FROM dbo._InfoRgSL296 T2

       WHERE ((((T2._Fld277 = @P1) AND(T2._Fld278 = @P2)) AND(T2._Fld279 = @P3)))

       ) T1

Бинго! Вот она, наша таблица итогов - _InfoRgSL296. Видим, что запрос стал гораздо меньше и проще. И его можно сделать еще более эффективным, если индекс таблицы итогов станет кластерным. Будем ждать от 1С этой оптимизации.

Рассмотрим ситуацию, когда число записей в самом регистре очень близко к числу записей в таблице среза. Например, данные в вашем периодическом регистре меняются очень редко и в 90% случаев он содержит только одну запись для уникальных значений измерений, а в остальных 10%: 2-3 записи. В этом случае использование итогов лишено смысла т.к. теряется основной профит - нет сокращения объема читаемых данных.


Выводы: 

  1. Простая конструкция СрезПоследних может легко превратиться в достаточно сложный запрос с двумя, а то и с тремя уровнями вложенности. Это необходимо иметь в виду, особенно при соединении срезов с другими таблицами и вложенными запросами.
  2. Включение итогов по срезам поможет оптимизировать только те запросы, где срез делается без указания даты /..СрезПоследних(, ...)../, в противном случае - напрасные накладные расходы на поддержание актуальности таблиц итогов.
  3. Периодичность "По позиции регистратора" следует использовать осторожно из-за сложного запроса получения среза. Если согласно закладываемой логике два документа, в одну и ту же секунду, НЕ могут сделать запись с одинаковыми значениями измерений, то использование этой периодичности является избыточным.

Заключение.

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

Пишите, чем еще можно дополнить исследование, какие моменты раскрыть более детально. Дополняйте статью комментариями о своем опыте и фишках.

Успехов!

#Содержание

См. также

Метод Дугласа-Пойкера для эффективного хранения метрик

Математика и алгоритмы Платформа 1C v8.2 Конфигурации 1cv8 Россия Абонемент ($m)

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

1 стартмани

30.01.2024    1753    stopa85    12    

33

Алгоритм симплекс-метода для решения задачи раскроя

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

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

19.10.2023    4415    user1959478    50    

34

Регулярные выражения на 1С

Математика и алгоритмы Инструментарий разработчика Платформа 1С v8.3 Мобильная платформа Россия Абонемент ($m)

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

1 стартмани

09.06.2023    7449    4    SpaceOfMyHead    17    

56

Модель распределения суммы по базе

Математика и алгоритмы Платформа 1С v8.3 Россия Абонемент ($m)

Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса

1 стартмани

21.03.2022    7848    7    kalyaka    11    

44

Изменения формата файлов конфигурации (CF) в 8.3.16

Математика и алгоритмы Платформа 1С v8.3 Бесплатно (free)

Дополнение по формату файлов конфигурации (*.cf) в версии 8.3.16.

16.12.2021    4443    fishca    13    

36

Интересная задача на Yandex cup 2021

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

Мое решение задачи на Yandex cup 2021 (frontend). Лабиринт. JavaScript.

12.10.2021    8828    John_d    73    

46

Механизм анализа данных. Кластеризация.

Математика и алгоритмы Анализ учета Платформа 1С v8.3 Анализ и прогнозирование Бесплатно (free)

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

31.08.2021    7796    dusha0020    8    

70
Вознаграждение за ответ
Показать полностью
Отзывы
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 дня и т.п.

Настройки можно менять при необходимости по ходу жизни системы в связи с добавлением статусов или при изменении статистики
перехода статусов и нагрузки на базу.
user645801_yyyuuu123q; alk; V4L; Ruden95; Sergey.Noskov; speshuric; +6 Ответить
Остальные комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
70. MaxMNSH 02.09.16 14:06 Сейчас в теме
(40) speshuric, (66)
По поводу регистра накопления, так и остался открытым главный вопрос.
Исходя из условий задачи, я понял, что регистр накопления мы полностью исключаем ( в задаче есть косвенное указание, что нельзя использовать никакие регистраторы)

speshuric, прав ли я что нельзя никоим образом использовать регистры накопления по условию задачи?

Хотя для решения регистр накопления,конечно, оказался бы далеко не лишним что бы получить на любую дату остаток по статусам и заявкам использую итоги
71. Sergey.Noskov 1376 02.09.16 14:46 Сейчас в теме
(70) MaxMNSH, жесткого запрета нет, но используя регистр накопления сложно обеспечить условие:
>Размер БД должен расти не больше чем O(n*ln(n)). Уж точно не квадратично.
76. Ovrfox 14 02.09.16 17:16 Сейчас в теме
(71) Пока все решения без регистра остатков записывают больше записей. Я не уверен , что в регистре остатков к-во записей больше, чем ограничение.
n описывает к-во дней? Или к-во Заказов?
Если заказов, то все зависит от того, будет или нет неограниченное по дате состояние заказа. Если ВСЕ заказы закрываются меньше чем за 1 год, а заказов больше 1 миллиона, а периодичность остаткков 1 месяц, то условие выполняется с запасом.
164. sss999 48 08.09.22 23:20 Сейчас в теме
163. sss999 48 08.09.22 23:18 Сейчас в теме
(37) очень странно что я пишу записать(истина) а он мне такие измерения есть, так а зачем я истина поставил, так замести..это бред
30. ildarovich 7850 10.08.16 14:03 Сейчас в теме
(25) speshuric, (27) присоединяюсь к вопросу, очень интересно.
Мне казалось, что РС - это одна из самых простых и понятных структур.
С чтением вроде бы вообще все хорошо.
С индексами на все случаи не угодишь, когда период последний в индексе, это не во всех случаях лучше, да и физике не отвечает, в которой привычнее первым время действия называть.
С записью MERGE, наверное, не хватает.
Так что про "костыльность" было бы очень интересно узнать.
162. sss999 48 08.09.22 23:14 Сейчас в теме
(31) я искал про регистр сведений инфу, ранее видел, что он блокирует весь регистр на запись если записывает что либо через набор или менеджер.
28. progr-2008 118 10.08.16 11:03 Сейчас в теме
Полезная статья, много нюансов.
32. KulSer 13.08.16 12:36 Сейчас в теме
Можно подробнее узнать про 8.2?
Если разработчики заложили использование индекса [Период + Измерение1 + Измерение2 + Измерение3], то ведь какая-то логика в их рассуждениях была?
Ведь про <= не вчера стало известно.
33. Sergey.Noskov 1376 15.08.16 12:02 Сейчас в теме
(32) KulSer, подробнее хорошо бы узнать у разработчиков платформы ;)
разработчики заложили использование индекса [Период + Измерение1 + Измерение2 + Измерение3]

не совсем так, разработчики целенаправленно создали два индекса и как раз второй [Измерение1 + Измерение2 + Измерение3 + Период] и предполагался к использованию. И задачу свою, по большому счету, этот индекс выполняет. Другой вопрос, почему кластерным был сделан именно индекс с периодом на первом месте... скорее всего руководствовались принципом, чтобы порядок полей хоть как то соответствовал порядку вставки новых строк в таблицу. Проще говоря, период на первом месте будет обеспечивать чуть меньшую фрагментированность данных в индексе.
Vladimir Litvinenko; +1 Ответить
34. KulSer 16.08.16 23:37 Сейчас в теме
35. Rik30 14 19.08.16 12:42 Сейчас в теме
Соглашусь с комрадами. Однозначно в закладки.
41. speshuric 1326 26.08.16 01:56 Сейчас в теме
Но, естественно, cf не нужен если все согласны, что условия в такой структуре можно выполнить. Если есть обоснованные сомнения, то код решает.
47. пользователь 26.08.16 17:55
Сообщение было скрыто модератором.
...
57. white-mount 01.09.16 16:05 Сейчас в теме
Уважаемый Sergey.Noskov, начинаю читать Вашу статью и сразу же спотыкаюсь на вопросе " Как оптимально выбрать структуру регистра? ".
Разве не регистраторы определяют структуру регистра сведений?
58. Sergey.Noskov 1376 01.09.16 16:29 Сейчас в теме
(57) white-mount,
интересный вопрос.. а еже ли регистратора нет в принципе? Например курсы валют? или данные в регистр заносит пользователь ручками?
Структуру регистра должны определять условия задачи, которую решает программист.
61. white-mount 01.09.16 18:32 Сейчас в теме
(58)
" а еже ли регистратора нет в принципе? Например курсы валют?или данные в регистр заносит пользователь ручками?
Структуру регистра должны определять условия задачи, которую решает программист. "
Если нет регистратора, то структура регистра определена бизнеслогикой, "задачей". Сам регистр, как сущность, вторичен. Если запись единична, на нет и суда нет.
В статье речь идёт не о методах записи в регистр сведений, а об использовании его, как сущности. Я так понял.

(59) white-mount, пост прицепился не к той статье. как перенести или удалить пост?
62. Sergey.Noskov 1376 01.09.16 21:40 Сейчас в теме
(61) white-mount,
Если нет регистратора, то структура регистра определена бизнеслогикой, "задачей"

Что меняет появление регистратора?
63. white-mount 02.09.16 10:52 Сейчас в теме
(62)
Что меняет появление регистратора?
Прежде всего упорядочивает изменение записей.
История - кто, когда...
Второе, регистратор, как сущность важен для аналитики. Именно это даёт понимание как нужно конструировать регистр сведений.
Вы упомянули регистр курсов валют, но он записывается при обращении к справочнику служебной обработкой, кроме ввода начальных остатков. Это ведёт к усложнению авторизации, разрастанию модулей. Одно дело модуль прописан самой 1С, и другое когда модули пишут в разное время разные программисты. Как результат - мусор в таблицах. Особенно усугубляющийся добавлением и последующим удалением измерений, ресурсов. Всплывает при реструктуризации ИБ, либо при поиске источника ошибок. Попробуйте удалить из регистра измерение и посмотреть на структуру cf. "дыра" с уникальным идентификатором остаётся.
Именно поэтому, читая стартовый абзац меня и резанула фраза -"Как оптимально выбрать структуру регистра? ".
Хорошо хоть согласились, что структура регистра вторична, а "задача" первична.
Если на первом месте регистраторы, то это статистика, на основании её можно ранжировать измерения и ресурсы. На этом этапе чаще всего начинающие совершают ошибки и зацикливают ссылки. Пример - использование справочника "договора", в котором в качестве реквизита есть подчинённый справочник ..., с реквизитом "договора".
Таким образом приходим к выводу - только имея описание процесса, можно понять какие сущности в качестве измерений, и как часто, будут использованы.
Разумеется сказанное относится только в части регулярного использования регистра сведений.

Тогда начинать статью с описания искусства описания бизнес-логики заказчика?
65. Sergey.Noskov 1376 02.09.16 12:15 Сейчас в теме
(63) white-mount,
Именно поэтому, читая стартовый абзац меня и резанула фраза -"Как оптимально выбрать структуру регистра? ".
Хорошо хоть согласились, что структура регистра вторична, а "задача" первична.

Есть подозрение, что вы как по своему поняли эту фразу... Вы хотя бы выводы прочитали? вынужден процитировать сам себя:

Из этого примера можно сделать очень интересный вывод - спроектировать оптимальную архитектуру регистра можно только при известном пуле запросов, которые будут к нему обращаться, и понимании как будут распределяться данные !

Задам наводящие вопросы
1. правильно понимаю, что программист первым делом должен ответить на вопрос нужен регистратор или нет?
2. правильно понимаю, что вопрос "будет ли регистр подчиненным или нет" никоим образом не относится к области проектирования (выбору) структуры регистра?
2. правильно понимаю, что если регистр будет подчиненным регистратору, то вопросы состава его измерений и ресурсов, их порядок следования и необходимость индексирования вообще не важны и могут быть хаотично-произвольными?
74. white-mount 02.09.16 17:00 Сейчас в теме
(65)
Уважаемый,
Есть подозрение, что вы как по своему поняли эту фразу... Вы хотя бы выводы прочитали?

Ваша фраза
"Как оптимально выбрать структуру регистра? "

Давайте ка обратимся к классу объектов метаданных(дерево конфигурации), предоставляемых платформой 1с 8.х.
Программист волен выбрать из дерева конфигурации( объектов, сущностей, метаданных) тип регистра и задать, или нет, из наследуемых свойств, нужное количество измерений и ресурсов.
Единственное, в чём волен программист - это выбор типов данных и их ранжировка, порядок следования, т.е. в итоге очередность обращений. Закон наследования ещё никто не отменял.
Думаю, что вы в статье слишком широко представили понятие "выбрать", нужно конкретно писать в семантике 1С. Теория вообще, это там. В 1С всегда свои частности.
Таким образом плавно возвращаемся к сути регистра сведений.

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

Разработчик пишет следующее:
1.3.2.2.12. Регистры
Регистры предназначены для хранения и обработки различной информации,
отражающей хозяйственную или организационную деятельность предприятия и
не имеющей объектной природы.
В регистрах обычно хранится информация об изменении состояний объектов или другая
информация, не отражающая непосредственно объекты предметной области. Например, в
регистрах может храниться информация о курсах валют или информация о приходе и расходе
товаров.
В системе «1С:Предприятие» существует 4 вида регистров:
● регистры сведений,
● регистры накопления,
● регистры расчетов,
● регистры бухгалтерии
Показать


Останавливаемся, и разбираем. Ибо имея адрес памяти, строку в таблице записи, можно к нему и адресоваться, не так ли?

1. регистр сведений - это объект, сущность, метаданных, предназначен - читаем разработчика.
А как он хранит информацию, как обрабатывает?
Вы пишете
что все данные базы хранятся именно в СУБД.

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

А если у измерения тип неограниченная строка это как то сильно влияет на скорость "вставки"?
За "вставку" отвечает файловая система операционки, или сервер SQL, или сервер 1С, запросивший эти ресурсы?
Менеджер чтения/записи это функционал СУБД 1С. Структура и тип регистра предопределены конфигурацией. Значит понятие быстродействия это в первую очередь переменные окружения, машинерия. Согласны?
Ваши вопросы ко мне
Задам наводящие вопросы
1. правильно понимаю, что программист первым делом должен ответить на вопрос нужен регистратор или нет?
2. правильно понимаю, что вопрос "будет ли регистр подчиненным или нет" никоим образом не относится к области проектирования (выбору) структуры регистра?
2. правильно понимаю, что если регистр будет подчиненным регистратору, то вопросы состава его измерений и ресурсов, их порядок следования и необходимость индексирования вообще не важны и могут быть хаотично-произвольными?

1) - нет, не правильно.
Ибо первый вопрос - это задача и её формализация, классификация объектов, их ранжировка.
2) вопрос каким конкретно будет регистр сведений, вне контекста задачи, смысла не имеет.

3) нет, не правильно, ибо
индексирование, если оно выбрано в свойствах, есть функционал от созданной структуры. Регистратор упорядочивает записи, каталагизирует, инициирует запись. Но саму запись или чтение осуществляет менеджер, приняв параметры. Если индексирование не прописано, то менеджер действует по своему алгоритму, а он для изучения широкой публике недоступен. Отлавливать структуру параметров это далеко за границами данной статьи.

"хаотично-произвольными" записи могут быть и часто такое бывает, и именно при использовании самописных обработок, либо при расширении списка измерений или ресурсов, но без обработки записей.
88. Sergey.Noskov 1376 02.09.16 19:28 Сейчас в теме
(74) white-mount,
кажись начинаю понимать ваш тонкий юмор и троллинг над созвучными терминами в классических языках программирования))
Останавливаемся, и разбираем. Ибо имея адрес памяти, строку в таблице записи, можно к нему и адресоваться, не так ли?
Моя фраза "НЕ ссылочного типа" и выдержка из документации "не имеющей объектной природы" тождественны т.к. используемые термины "Ссылка" и "тип" относятся сугубо к 1С. Что расшифровывается на примере в продолжении фразы, которую вы предусмотрительно не процитировали:
Основное назначение регистров сведений - хранить информацию НЕ ссылочного типа в разрезе комбинации измерений и, как частный случай, хранить изменяемые во времени значения. По большому счету, в регистре можно хранить что угодно, но получить ссылку на саму запись регистра и сохранить её (ссылку на запись) в реквизитах другого объекта, например справочника, уже не получится.
понятно ведь, что когда мы говорим про "сохранить в реквизит справочника", мы не адрес памяти имеем ввиду. Согласны? И почему вы не докапываетесь до термина "объектная природа" рассматривая её в семантике 1С, но докапываетесь до термина "ссылка" используя семантику (условно и только в качестве примера)С++? Что это за "враждебные двойные стандарты"(с) ?

Единственное, в чём волен программист - это выбор типов данных и их ранжировка, порядок следования, т.е. в итоге очередность обращений. Закон наследования ещё никто не отменял.
Думаю, что вы в статье слишком широко представили понятие "выбрать", нужно конкретно писать в семантике 1С.
Так это вы копаете куда то в глубь, в статье нет намеков на широту понятия. Речь только про конфигуратор, смысл писать про классы, наследование т.п.? Еще раз, под "структурой регистра" подразумевается состав измерений, порядок их следования, индексация, периодичность регистра, его подчиненность регистратору (*в первую очередь ориентируясь на решаемую бизнес-задачу!) для объекта метаданных "Регистры сведений" в приложении Конфигуратор программного комплекса 1С:Предприятие.

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

Структура и тип регистра предопределены конфигурацией. Значит понятие быстродействия это в первую очередь переменные окружения, машинерия. Согласны?
Нет. Для программиста 1С на первом месте конфигурация.

Подводя итог, просьба при рассмотрении статьи не углубляться до уровня абстракции разработчика самой платформы а ограничиться терминологией 1С и теорией реляционных баз данных, попытка навязать терминам другую смысловую нагрузку будет расценена как не прикрытый троллинг. У меня не было цели "раскопать исходники", ибо программисту 1С эта информация ни как не поможет. Хотя если вы знаете чем это может помочь и способны изложить - ждем статью.
89. white-mount 02.09.16 22:10 Сейчас в теме
(88) уважаемый,

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

Думаю, что пора прекратить, Вы начинаете меня подозревать в троллинге.
Я всего лишь следую методу - "вначале лошадь, затем телега".

Вывод о том, что статья полезна для освежения знаний полена, подтверждаю.
90. Sergey.Noskov 1376 02.09.16 22:54 Сейчас в теме
(89) white-mount, я много о чем умолчал, когда речь идет о связке таких сложных вещей как сущности 1С, СУБД и теория реляционных БД сложно оперировать терминами низкого уровня - объемы информации, деталей, отсылок к словарям и википедиям поглотят любую благую задумку, поэтому волей-не волей приходится придерживаться неких абстракций и упрощений. В рамках этих упрощений СУБД и хранит и записывает и читает и даже транспортирует эти данные. Несомненно, что данные "лежат" (опять таки упрощение) не в СУБД, а в файле, да и файлов этих может быть несколько и их назначение так же может быть разным... Но вопрос в том, как эта информация помогает в работе с регистрами? мне кажется - никак.
Ну а если мы с вами начнем выяснять кто же на самом деле хранит данные, рассматривая "под лупой" сам термин "хранение", операционную систему, драйвера, различные варианты дисковых систем и их железо, включая RAM и SSD диски, боюсь мы просто придем к бессмысленному и беспощадному холивару.

Без сарказма, рад, что для вас статья это только освежение знаний, заметно образование, что называется, по специальности, но чаще всего программисты 1С не знают про то, что происходит за абстракцией "конфигуратор" и не видят разницы в записи подчиненного и независимого регистра. Я сделал попытку спуститься на ступеньку ниже, до уровня абстракции "СУБД" и показать, что происходит с данными на этом этапе. Кажется, что мне таки это удалось.
Dementor; PrinzOfMunchen; speshuric; white-mount; Allexe8.1; +5 Ответить
59. white-mount 01.09.16 18:01 Сейчас в теме
1. Статья полезна для освежения знаний.
2. В статье общетеоретические сведения часто заменяют частности из 1С, что на мой взгляд не совсем правильно. Раз статья посвящена регистрам сведений 1С, то и разбирать нужно только 1С.
3. Говорить об индексах и о скорости исполнения запроса, вне контекста режима использования 1С, не имеет смысла.
4. Изучать мат часть нужно по оригинальной документации.

Ибо натолкнувшись на утверждение, подобное "Регистр сведений на уровне СУБД представляет собой обычную плоскую таблицу, в которой колонки это наши измерения, ресурсы и реквизиты, а строки - записи регистра. ", можно сделать в отношении файлового режима, файл "*.1CD", несколько неправильные выводы.
60. Sergey.Noskov 1376 01.09.16 18:31 Сейчас в теме
(59) white-mount, спасибо за мнение.
2. В статье общетеоретические сведения часто заменяют частности из 1С, что на мой взгляд не совсем правильно. Раз статья посвящена регистрам сведений 1С, то и разбирать нужно только 1С.

не совсем понятно о чем речь, можно пример?
3. Говорить об индексах и о скорости исполнения запроса, вне контекста режима использования 1С, не имеет смысла.

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

тезис "Регистр сведений на уровне СУБД представляет собой обычную плоскую таблицу, в которой колонки это наши измерения, ресурсы и реквизиты, а строки - записи регистра" на 100% соответствует и формату *.1CD и именно поэтому написано СУБД т.к. *.1CD ничто иное, как файл реляционной базы данных, просто роль СУБД, в данном случае, выполняет сама платформа.
79. Ovrfox 14 02.09.16 17:39 Сейчас в теме
Продолжу
вероятность перехода через остатки для статуса
20% заявок переходят примерно через 5 минут
20% заявок переходят примерно через 30 минут
20% заявок переходят примерно через 2 часа
20% заявок переходят примерно через 8 часов
10% заявок переходят примерно через 3 суток
9% заявок переходят примерно через 15 суток
1% заявок переходят примерно через 30 суток,
где n - это именно к-во заявок

0,2 * 5/45000 * n * 3 +
0,2 * 30/45000 * n * 3+
0,2 * 120/45000 * n * 3+
0,2 * 480/45000 * n * 3+
0,1 * 3/30 * n * 3+
0.09 * 15/30 * n * 3+
0.01 * n * 3
Это примерно 0,068466667 * n
83. Ovrfox 14 02.09.16 18:03 Сейчас в теме
(79) Ovrfox, Исправлюсь
примерно 0,04 *n +0.03 n от всех предыдущих заказов, т.е. для 268 тыс. в месяц к-во записей будет за 10 лет
(0.04 * n * 120 + 0.03 *n *120 * (120 + 1) /2) / (120 * n) = 222.6
Считаем логарифм от 268 тыс * 120 - получаем 14.
Математика явно не в пользу метода
85. Ovrfox 14 02.09.16 18:17 Сейчас в теме
(83) Ovrfox, (77) Извините, логарифм действительно 18
Но даже если мы не будем учитывать тройку по к-ву статусов, то все равно коэффициент увеличения (т.е. только таблица остатков регистра) будет более 70
Проверьте еще раз свои расчеты. Ваш коэффициент не растет с ростом БД. Это явная ошибка
93. MaxMNSH 05.09.16 13:45 Сейчас в теме
(85) Ovrfox,

1.Просьба,внимательно прочитать предложенное решение и расчет. (учесть начальные условия которые я описал в расчете, в частности Nдней = 31 (что дает нам процент зависших <= 1%), тут не верно складывать всю статистику, как делали вы своем расчете. )

2.таблицы остатков в моем решении нет совсем. Значит и нет ее роста

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

и учтите, я не даю в расчете ФОРМУЛУ роста, я делаю лишь численный расчет при самых плохих начальных условиях и даю гарантию что реальная формула роста даст всегда меньшее число, а так же делаю вывод о том, что на более большой период (более 50 - 100 лет) рост базы может выйти за рамки n*ln(n) . Но на время реальной жизни (лет 50) база уложится в заданное условие роста
94. Ovrfox 14 05.09.16 18:10 Сейчас в теме
(93) MaxMNSH, Я ошибся, думал Вы давали оценку решения Ильдаровича
Для предложенного вами решения оценка, конечно же, будет другой. Но все равно Вы ошиблись.
Итак в день к-во заявок n , статусов 3 из них один конечный.
Итого записей в истории будет 3*n * Дней
Столько же будет во втором регистре
В день по заказам может быть зависшим только 0.01 процент заказов, он не может зависнуть в двух статусах, только в одном из двух не конечных, но обязательно в одном.
Значит каждый день зависает 0,01*n записей (одна запись на заказ)
Грубый (Ваш вариант ) коэффициент будет (2*3n*Дней + 0,01*3n*Дней*ДнейЗависания/31)/3n*дней это примерно 2+0,01*120 = 3,1 за 10 лет
Более праильный будет ДнейЗависания от 1 до Дней т.е. фактически 2+0,01/31(дней+1)/2 = 2.6 за 10 лет (мы помним, лог n примерно 18)
Для трех лет (по условиям задачи) это будет 2+0,01 * 18 = 2.18 ,а логарифм будет примерно 17
Для 50 лет логарифм достигнет 20, а коэффициент по вашей методике будет 5
95. Ovrfox 14 05.09.16 18:28 Сейчас в теме
(93) MaxMNSH, При этом где -то ошибся и я
В варианте с остаткими, кроме 2-х историй по регистру сведений и по регистру остатков (при этом по регистру остатков история не полная, т.к. отсутсвует расход третьего статуса - он конечный) это уже коэффициент (1+ 5/3 = 2.667) Случайному переходу через остатки подвержены, согласно статистике 0,0228 записей (из расчета к-во заказов *3 = к-во записей). Кроме того 1% заказов зависает на всегда - тогда
в первый месяц зависает 0,01n заказов на (все меясцы -1), а в предпоследний месяц - 0,01n заказов на 1 месяц итого для 10 лет
119*120/2 = 71,4n т.к. всего записей 3*120*n, то коэффициент составит 0,19833 итого общий коэффициент был бы всего 2.865, если бы не проблема последнего статуса. Он ведь остается в остатках навсегда.
Т.е. реальный коэффициент 1 + 5/3 +1/3*(К-во месяцев+1)/2 = 8,667 даже для трех лет (22.667 для 10). Но по условиям задачи (для трех лет) коэффициент меньше Log(n) , который около 10.
100. amadeus2011 07.09.16 01:15 Сейчас в теме
довольно интересная статья, достаточно подробно описано назначение регистра сведений
119. speshuric 1326 13.09.16 15:32 Сейчас в теме
Пробую еще раз поставить вознаграждение.

ЗЫ: не получилось. Значит сейчас посмотрим что можно сделать.
120. oafan 27 15.09.16 08:20 Сейчас в теме
Статья полезная, но русский язык как всегда страдает. Наверное нужно написать на Infostart - обязательная проверка word-ом перед публикацией.
121. Sergey.Noskov 1376 15.09.16 11:19 Сейчас в теме
(120) oafan, спасибо за оценку! Проверял не только word-ом и не один раз. Текста много, мог что-то пропустить. Если не затруднит, напишите в личку, поправлю.
122. speshuric 1326 16.09.16 15:08 Сейчас в теме
Наконец-то награда нашла героя. С меня разбор и обзор вариантов "вообще".
123. MaxMNSH 16.09.16 18:41 Сейчас в теме
(122) speshuric, (117) Ovrfox, Все нормально. Награду получил)
131. HAMMER_59 244 28.09.16 16:02 Сейчас в теме
Лично меня пугает - срез последних.
Не представляю как SQL отбирает нужные данные.
Как можно определить на какой записи индекса нужно остановится?

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

Пример:
РегистрСведений.ЦеныНоменклатуры
Измерения: Номенклатура, ХарактеристикаНоменклатуры, ТипЦен
Если мы укажем отбор только по Номенкалатуре, тогда SQL переберет все варианты ХарактеристикНоменклатуры? SQL ведь не знает о подчиненности.

Для 8.2 совсем не понятно. Фактически должен перебрать все записи с датой меньше равной дате среза.

В принципе мне ничего не мешает SQL Profiler поглядеть сколько там записей получается:
Фактическое количество строк 9467 - это я в отборе указал 1 позицию номенклатуры.

132. Sergey.Noskov 1376 28.09.16 16:59 Сейчас в теме
(131) HAMMER_59,
Если мы укажем отбор только по Номенкалатуре, тогда SQL переберет все варианты ХарактеристикНоменклатуры? SQL ведь не знает о подчиненности.
SQL о подчиненности и не должен знать, все решает архитектура индекса, в нем всегда содержится комбинация значений, поэтому если характеристика N не подчинена номенклатуре Т, то для Т в индексе [Номенклатура, Характеристика] не будет значения N (все таки советую поизучать как работают индексы, в приведенной ссылке на видео объяснено очень наглядно).
Для 8.2 совсем не понятно. Фактически должен перебрать все записи с датой меньше равной дате среза.
Это не так, всегда есть индекс по измерениям с Периодом на последнем месте.
133. serg_infostart 386 01.11.16 14:37 Сейчас в теме

Периодический, с периодичностью "По позиции регистратора"
Измерение + Период + Регистратор + НомерСтроки + [Активность]* Измерению "Измерение" задано свойство "Индексировать".

Правильно я понимаю, что флаг "Ведущее" тут роли не играет вообще?
134. Sergey.Noskov 1376 01.11.16 17:24 Сейчас в теме
(133) serg_infostart, свойство "Ведущее" так же будет приводить к созданию индекса.
В статье поправлю, спасибо.
135. ture 606 07.11.16 15:34 Сейчас в теме
(0) при всём уважение к автору немного офтопа:
- нужно 1С разрешить нам описывать таблички и индексы
- нужно 1С разрешить нам создавать классы для работы с табличками и индексами (только для SQL)

И вот с этими пунктами мы сами будем рисовать материал для публикаций на тему "вскрытие покажет". Кроме того этого не хватает для ерп.
137. Sergey.Noskov 1376 09.11.16 13:43 Сейчас в теме
(135) ture, всегда хочется больше свободы. Скорее всего этого у нас не будет, ну максимум 1С даст свободу в описании структуры индексов.
136. Pipapalamm 09.11.16 11:37 Сейчас в теме
Это... Это просто... ШШШШШШШШШИКАРНО!
Спасибо!
138. aKomper 05.01.17 14:23 Сейчас в теме
Сергей, спасибо - шикарная статья, здорово экономит время.
139. demart-omsk 20 07.08.17 16:11 Сейчас в теме
Спасибо, познавательно!

По традиции 05.08.2017 пропустили статью) Ждем новой информации, в том же духе, для подписчиков!
140. Dm_Kz 21.09.17 11:47 Сейчас в теме
А где же обещанное, после выхода релиза 8.3.8.2014, дополнение статьи?
141. Sergey.Noskov 1376 21.09.17 18:54 Сейчас в теме
(140)хорошо, что есть спрос.
Готовился к докладу, обязательно допишу!
142. Dm_Kz 22.09.17 10:13 Сейчас в теме
(141)
хорошо, что есть спрос.

Конечно есть! Будем ждать, спасибо!
143. Darius 13.10.17 09:11 Сейчас в теме
Учитывая кучу глупых ошибок в правописании, я бы не доверял этой статье.
144. tormozit 7136 13.10.17 09:19 Сейчас в теме
(143) Может лучше обозначить их и сделать тем самым всем лучше?
145. Darius 14.10.17 10:48 Сейчас в теме
(144) Ага, больше заняться нечем.
146. Evil Beaver 8107 15.10.17 19:19 Сейчас в теме
(145) Время на бессмысленные комментарии, однако, находится, да.

А если бы вы потратили еще немного времени и посмотрели на автора статьи, то про доверие к ней мысли бы не возникло.
147. cw014g 25.01.18 11:16 Сейчас в теме
Интересно, зачем такие сложности при записи набора записей? Именно из-за них не получается сделать отборы к примеру по виду сравнения периода больше или меньше
148. Sergey.Noskov 1376 25.01.18 11:43 Сейчас в теме
(147)
Интересно, зачем такие сложности при записи набора записей?
а как проще? Вроде ничего лишнего платформа не делает.
149. cw014g 25.01.18 12:53 Сейчас в теме
(148) Допустим, я хочу перезаписать не все записи, а начиная с определенного периода. Для этого придется подключать выборку или запрос к базе данных, при которых будут сплошняком несколько команд DELETE и несколько команд INSERT. Если же обработку видов сравнения в отборе набора записей смогли бы обработать видом запроса (к примеру "Больше" - это ">"; "БольшеИлиРавно" - ">=" и т.д.) получилось бы, что на перезапись части регистра потребовалось гораздо меньше команд и как следствие нагрузки на сервер БД
150. Sergey.Noskov 1376 25.01.18 13:51 Сейчас в теме
(149) идея понятна. Но нагрузка измеряется не числом команд, а их ресурсоемкостью. А итоговая ресурсоемкость при порционной перезаписи будет близка к одной "объединенной" команде. Для СУБД иногда лучше выполнить 100 запросов по 100Мб данных, чем один большой на 10Гб. А вот время может отличаться заметно, при одной команде, если СУБД сможет "пережевать" за раз суммарный объем данных, будет быстрее.

Получается упрек не про запись набора, а про ограничение свойства ОТБОР у набора записей. Отбор всегда устанавливается только на равенство. Тут согласен, как минимум Период можно было бы фильтровать по интервалам.
151. user902916 29.01.18 09:12 Сейчас в теме
самая тяжелая тема для меня эти регистры
152. Юрий-К 112 12.03.18 14:36 Сейчас в теме
Когда то так же пришлось докопаться до истоков по регистру сведений.

Столкнулся с неожиданным (для себя) поведением конструкции СрезПоследних при составном регистраторе.

При установке отбора в конструкции
   ГДЕ  Таблица.Регистратор ссылка  Документ.Документ1 


,а не в условиях отбора в "виртуальных таблицах" СрезПоследних .
153. Sergey.Noskov 1376 13.03.18 08:01 Сейчас в теме
(152) а в чём обнаружили неожиданность? Делитесь)
speshuric; +1 Ответить
154. MirrorVL 24.10.18 06:27 Сейчас в теме
Просьба автора поправить в статье запросы по получению аналога среза последних. Он допустил ошибку не указав условие что Активность = Истина, классический вложенный запрос который генерит платформа 1с вставляет условие _Active = 0x01 при обращении к таблице регистра.
155. Sergey.Noskov 1376 26.10.18 19:04 Сейчас в теме
(154) Не все так просто. Если у регистра установлено свойство "Разрешить итоги: срез ...", то условия по активности нет (сейчас проверил на 8.3.10.2375, тот же эффект). Всё что приведено статье - трассы с реальных запросов. Поэтому и аналог писался с трассы.
Вообще интересное поведение, не обращал внимание. Возможно такая задумка, возможно ошибка. Надо поизучать вопрос, проверить поведение на более свежих релизах и описать этот момент в статье.
Спасибо!
156. herres 15.11.19 16:20 Сейчас в теме
Почему если в запрос РегистрНакопления.Товары.Остатки(&Момент) передать момент времени документа, то остатки получатся на границу НЕ ВКЛЮЧАЯ движения документа,

а если в запрос РегистрСведений.Данные.СрезПоследних(&Момент,) то срез получится на границу ВКЛЮЧАЯ движения документа ?

Разве это логично ? Разве это не ошибка в платформе ?
157. Sergey.Noskov 1376 20.11.19 13:57 Сейчас в теме
(156) Этим можно управлять. Достаточно передать в качестве параметра объект "граница" и в её параметрах как раз и указать надо включать границу или нет.
158. user1519152 15.01.21 05:14 Сейчас в теме
Чем сложнее задача, тем меньше пользы от высокоуровневых инструментов и больше вреда от их ограничений.
Я бы попробовал просто мс скл таблицу как минимум, получив более богатый язык запросов. В 1с он остановился в 2000году. Ну или посмотрел в сторону но-скл или аналитических СУБД, что в сотни раз скорость запросов может поднять. Разобраться с простым неодинэс вебсервисом мне кажется на порядок проще и в разработке и обслуживании чем эта фигурная вышивка по метаданным 1с.
159. Sergey.Noskov 1376 18.01.21 15:46 Сейчас в теме
(158)
Чем сложнее задача..
"а судьи кто?" (с)
Где грань простоты? Да и сам по себе "мс скл" отнюдь не низкоуровневый язык программирования.
Радикальность мнения это как раз то, что мешает решать задачу. Любую.
Evil Beaver; +1 Ответить
160. user1679547 15.02.22 14:26 Сейчас в теме
(11)вы спалилилсь что вы автор))) там тоже через слово "логика" логично" аналогично)))
168. Wefast 11.11.22 12:28 Сейчас в теме
Про индексацию.

Измерение же индексируется только если стоит соответствующий признак?

У меня есть поля Документ, Дата, ПризнакБулево.

При записи уникальность записи опеределяется документом.

Запрос получать я буду данные по Дате и ПризнакуБулево.

Т.е. мне нужно индексировать Дату и ПризнакБулево.

Если я сделаю Дату и ПризнакБулево ресурсами, и укажу что они индексируются.

Будет ли все это корректно работать?

Или мне нужно все 3 поля сделать измерениями, указать что они индексируются и документ поставить третим?
169. Said-We 19.04.23 18:38 Сейчас в теме
(1) А такой схематический запрос будет аналогом среза последних или нет? :-)
sel ect
    *
fr om
    (select
           row_number() over(partition by t.izmer1, t.izmer2...t.izmerN ORDER BY t.period DESC) as npp
          ,t.period

          ,t.izmer1
          ,t.izmer2
           ...
          ,t.izmerN

          ,t.resurs_rekvizit1
          ,t.resurs_rekvizit2
           ...
          ,t.resurs_rekvizitN
     fr om
           PC_name as t
     where
           t.period <= &ДатаХ AND
           условия внутри скобок среза на поля <t.izmer1, t.izmer2...t.izmerN> и на поля <t.resurs_rekvizit1, t.resurs_rekvizit2,...t.resurs_rekvizitN>
     ) as t
wh ere
     t.npp = 1
Показать
170. speshuric 1326 20.04.23 00:12 Сейчас в теме
(169)
Ну будет, но с оговорками:
1. И у 1С, и у вас условия на ресурсы в срезе ставить можно, но это неправильно. Сами потом отлаживать и устанете.
2. "row_number() = 1" с большой вероятностью угробит производительность.
171. Said-We 20.04.23 10:00 Сейчас в теме
(170)
И у 1С, и у вас условия на ресурсы в срезе ставить можно

А я бы и не ставил, но в 1С действительно зачем-то можно.
row_number() - достаточно быстрый, даже по группировкам, тем более это измерения. В каком случае он угробит производительность?
172. Jungle Murzik 25.09.23 11:45 Сейчас в теме
почему нет кластерного индекса в таблицах срезов

По той же причине, почему нет индекса, когда нет измерений
Оставьте свое сообщение