Внутренние механизмы платформы. Часть 1

СУБД 1С Запросы Эксперт

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

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

СУБД 1С Запросы

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

СУБД 1С Запросы

Для удобства работы будем использовать MS SQL Server и инструмент Profiler, для других СУБД поведение будет примерно таким же. На уровне СУБД наша база теперь выглядит так:

СУБД 1С Запросы

Для остаточного регистра создалось 3 таблицы — непосредственно таблица регистра ([dbo].[_AccumRg46]), таблица опций ([dbo].[_AccumRgOpt51]) и таблица остатков ([dbo].[_AccumRgT50]). Обратите внимание, что номера у всех таблиц регистра разные.

В пользовательском режиме создадим две номенклатуры — «Стол» и «Стул», четыре склада, две серии, а также четыре документа, на каждый склад по одному документу.

Рассмотрим данные, которые были записаны в каждую из таблиц регистра накопления. Физическая таблица регистра:

В ней мы видим записи регистра, если посмотреть на колонку «_Period», то можно увидеть, что там год не 2021, а 4021. Так происходит из-за того, что 1С хранит дату как тип данных datetime, а в СУБД MS SQL этот тип данных хранит в диапазоне от 01.01.1753 до 31.12.9999. С обычными датами, где фигурирует дата и время, все в порядке, но 1С сохраняет время как дату, т.е. получается 01.01.0001 13:00, что уже приводит к ошибке.

Колонки, которые начинаются с префикса _Fld — это пользовательские поля, которые добавлены через конфигуратор. Колонка _RecorderRRef — это ссылка на документ, здесь все просто, его можно найти в соответствующей таблице документов.

СУБД 1С Запросы

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

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

СУБД 1С Запросы Эксперт

В таблице _AccumRgT50 хранятся остатки.

СУБД 1С Запросы Эксперт

Так как у нас все документы созданы по разным регистрам, то таблица остатков по содержанию совпадает с физической таблицей регистра. Попробуем перенести один из документов на другой склад. Получаем следующую картину:

СУБД 1С Запросы Эксперт

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

СУБД 1С Запросы Эксперт

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

Рассмотрим запросы к остаточному регистру. Для этого напишем в консоли следующий запрос и выполним его:

СУБД 1С Запрос Эксперт

Здесь мы получаем остатки по регистру по всем измерениям. Посмотрим что происходило на сервере СУБД:

СУБД 1С Запросы Эксперт

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

SELECT
T1._Period,
T1._UseTotals,
T1._ActualPeriod,
T1._UseSplitter,
T1._MinPeriod,
T1._MinCalculatedPeriod
FROM dbo._AccumRgOpt51 T1
WHERE T1._RegID = @P1

Второй запрос получает остатки:

SELECT
T1.Fld47RRef,
T1.Fld48RRef,
T1.Fld55RRef,
T1.Fld49Balance_
FROM (SELECT
T2._Fld47RRef AS Fld47RRef,
T2._Fld48RRef AS Fld48RRef,
T2._Fld55RRef AS Fld55RRef,
T2._Fld49 AS Fld49Balance_
FROM dbo._AccumRgT50 T2
WHERE T2._Period = @P1 AND (T2._Fld49 <> @P2) AND (T2._Fld49 <> @P3)) T1

Здесь мы видим, что получение остатков производится через вложенный запрос. При этом просто берутся записи из остатков. В условиях @P2 и @P3 отсекаются строки с нулевым количеством. Остальные же запросы — это получение представлений ссылок, которые мы видим в результате выполнения запроса в консоли запросов.

SELECT
T1._IDRRef,
T1._Description
FROM dbo._Reference36 T1
WHERE T1._IDRRef = @P1

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

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

СУБД 1С Запросы эксперт

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

SELECT
T3._IDRRef,
T3._Description,
T4._IDRRef,
T4._Description,
T5._IDRRef,
T5._Description,
T1.Fld49Balance_
FROM (SELECT
T2._Fld47RRef AS Fld47RRef,
T2._Fld48RRef AS Fld48RRef,
T2._Fld55RRef AS Fld55RRef,
T2._Fld49 AS Fld49Balance_
FROM dbo._AccumRgT50 T2
WHERE T2._Period = @P1 AND (T2._Fld49 <> @P2) AND (T2._Fld49 <> @P3)) T1
LEFT OUTER JOIN dbo._Reference36 T3
ON T1.Fld47RRef = T3._IDRRef
LEFT OUTER JOIN dbo._Reference37 T4
ON T1.Fld48RRef = T4._IDRRef
LEFT OUTER JOIN dbo._Reference54 T5
ON T1.Fld55RRef = T5._IDRRef

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

СУБД 1С Запросы Эксперт

В итоге получаем следующий запрос:

SELECT
T1.Fld47RRef,
T1.Fld49Balance_
FROM (SELECT
T2._Fld47RRef AS Fld47RRef,
CAST(SUM(T2._Fld49) AS NUMERIC(22, 2)) AS Fld49Balance_
FROM dbo._AccumRgT50 T2
WHERE T2._Period = @P1 AND (T2._Fld49 <> @P2) AND (T2._Fld49 <> @P3)
GROUP BY T2._Fld47RRef
HAVING (CAST(SUM(T2._Fld49) AS NUMERIC(22, 2))) <> 0.0) T1

В нем мы видим, что появляется группировка (GROUP BY) во вложенном запросе, количество суммируется (SUM) и выражается (CAST) как число длиной 22 и точностью 2, а также добавляется функция ИМЕЮЩИЕ (HAVING), которая выводит только ты строки, в которых количество не равно нулю. Это более тяжелые функции, чем просто получить значения, поэтому логично предположить, что регистр накопления желательно проектировать так, чтобы получение остатков в основном происходило в разрезе всех измерений.

Добавить комментарий

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

*

code