При разработке и оптимизации систем на платформе 1С:Предприятие 8 программисты часто сталкиваются с необходимостью понимать, как именно высокоуровневые объекты метаданных транслируются в физическую структуру таблиц СУБД (будь то MS SQL Server, PostgreSQL, IBM DB2 или Oracle). Существует множество мифов — например, о том, что перечисление на уровне СУБД является простым текстовым полем внутри другой таблицы, или о том, что для каждой константы создается отдельная таблица.
В этой статье мы подробно разберем реальную физическую структуру СУБД для платформы 1С 8.х. Мы выясним, как устроены перечисления, где на самом деле хранятся константы, каким образом СУБД справляется с составными типами данных и как эти знания помогают писать более производительные запросы.
Прежде чем погрузиться в теорию, рассмотрим, как мы можем на практике увидеть структуру таблиц нашей информационной базы. Платформа 1С предоставляет для этого встроенный программный инструмент — метод ПолучитьСтруктуруХраненияБазыДанных(). Этот метод возвращает таблицу значений с описанием всех таблиц и полей СУБД, которые соответствуют объектам метаданных нашей конфигурации.
Давайте посмотрим на пример кода, с помощью которого можно выгрузить эту структуру для анализа:
ТаблицаСтруктуры = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого СтрокаТаблицы Из ТаблицаСтруктуры Цикл
Сообщить("Объект метаданных: " + СтрокаТаблицы.ИмяМетаданных
+ " -> Таблица в SQL: " + СтрокаТаблицы.ИмяТаблицыХранения);
КонецЦикла;
Используя этот инструмент, мы можем сопоставить любой объект в конфигураторе с реальной физической таблицей в SQL.
Существует заблуждение, что перечисление (ПеречислениеСсылка) — это просто текстовое или числовое поле в таблице того объекта, который на него ссылается. На самом деле это принципиально не так.
На уровне СУБД каждое перечисление имеет свою собственную физическую таблицу. Если в вашей конфигурации создано перечисление СтатусыЗаказов, платформа выделит под него отдельную таблицу.
Имя таблицы: Таблицы перечислений именуются по шаблону _Enum<n>, где <n> — это внутренний уникальный порядковый номер объекта метаданных в конфигурации.
Структура таблицы перечисления: эта таблица является максимально облегченной и содержит всего два поля:
_IDRRef — уникальный идентификатор конкретного значения перечисления. Это поле имеет тип данных binary(16) (UUID в двоичном формате)._EnumOrder — числовое поле (индекс), определяющее порядок сортировки элементов перечисления. Этот порядок соответствует тому, как вы расположили элементы перечисления в дереве метаданных в конфигураторе.Важная особенность: в СУБД вы никогда не найдете текстовых названий элементов перечисления (таких как "Новый", "ВРаботе", "Выполнен"). Все эти синонимы и имена зашиты в конфигурацию платформы. Сама СУБД оперирует исключительно бинарными идентификаторами _IDRRef и порядком сортировки _EnumOrder. Платформа сопоставляет UUID из базы данных с метаданными "на лету" при выводе интерфейса пользователю.
Когда вы указываете реквизит с типом "ПеречислениеСсылка" в справочнике или документе, в таблице этого документа просто создается поле типа binary(16), в которое записывается ссылка на соответствующий UUID из таблицы _Enum<n>.
Если вспомнить старые версии платформы (например, 1С 7.7), там архитектура хранения констант была иной. В версии 1С 8.х разработчики применили красивое и эффективное решение. Отдельные таблицы под каждую константу не создаются, так как это плодило бы огромное количество мелких таблиц и перегружало бы системный каталог СУБД.
Вместо этого в базе данных создается одна общая таблица _Consts.
_Consts физически содержит всего одну строку, в которой хранятся текущие значения всех констант вашей системы.Исключением из этого правила являются только те константы, у которых в конфигураторе созданы подчиненные табличные части. Для хранения данных таких табличных частей платформа генерирует отдельные подчиненные таблицы формата _ConstsVT<n>, которые связываются с основной таблицей констант.
Вопрос хранения составных типов данных (когда один реквизит может принимать значения разных типов, например, "Число", "Строка", "СправочникСсылка.Контрагенты" или "ДокументСсылка.Реализация") вызывает больше всего вопросов у начинающих разработчиков. Создаются ли для этого дополнительные таблицы связей?
Нет, составной тип данных не создает отдельных таблиц. Вместо этого платформа расширяет структуру таблицы того объекта, в котором объявлен этот реквизит, создавая несколько физических полей под один логический реквизит.
Для каждого реквизита составного типа платформа генерирует набор полей с одинаковым префиксом (например, _Fld123), но с разными суффиксами, отвечающими за хранение конкретных типов данных:
_TYPE — обязательное поле (двоичные данные), содержащее код фактически записанного типа данных в этой строке. Благодаря этому полю платформа понимает, из какого именно столбца СУБД нужно прочитать данные для 1С._L (Logical) — колонка для хранения типа Булево._N (Numeric) — колонка для хранения типа Число._T (Time) — колонка для хранения типа Дата._S (String) — колонка для хранения типа Строка._RTRef (Reference Type) — хранит идентификатор таблицы СУБД, на которую указывает ссылка в данной конкретной строке (так называемый тип ссылки)._RRef (Reference) — содержит сам уникальный идентификатор (UUID) конкретной записи (например, ссылку на конкретного контрагента или договор).Давайте разберем наглядный пример: у нас есть реквизит ДокументОснование с составным типом, который может принимать значения: Число, Строка или СправочникСсылка.Контрагенты. В физической таблице СУБД под этот один реквизит будет выделено пять полей:
_Fld123_TYPE (код типа данных в текущей строке)_Fld123_N (значение, если записано число)_Fld123_S (значение, если записана строка)_Fld123_RTRef (идентификатор таблицы справочника Контрагенты, если записана ссылка)_Fld123_RRef (UUID конкретного контрагента из таблицы контрагентов)Когда вы записываете в этот реквизит число 150, платформа заполняет поле _Fld123_TYPE кодом типа "Число", а в поле _Fld123_N записывает значение 150. Остальные поля (_S, _RTRef, _RRef) остаются пустыми (заполняются дефолтными значениями SQL NULL или пустыми ссылками).
Понимание физического устройства составных типов объясняет, почему опытные разработчики рекомендуют использовать их с большой осторожностью, особенно в реквизитах, по которым планируется делать соединения в запросах.
Когда СУБД выполняет запрос с соединением по составному полю, ей приходится на низком уровне анализировать множество колонок и использовать сложные логические конструкции CASE WHEN. Схематично SQL-запрос, генерируемый платформой 1С при обращении к составному полю, выглядит примерно так:
SELECT
CASE
WHEN T1._Fld123_TYPE = 0x08 THEN T2._Description -- Если это ссылка на справочник
WHEN T1._Fld123_TYPE = 0x02 THEN T1._Fld123_S -- Если это строка
ELSE NULL
END AS ЗначениеРеквизита
FROM _Document100 T1
LEFT OUTER JOIN _Reference50 T2 ON T1._Fld123_RRef = T2._IDRRef AND T1._Fld123_RTRef = 0x00000032
Подобные конструкции существенно усложняют построение плана запроса оптимизатором СУБД. Это может приводить к сканированию таблиц (Table Scan / Index Scan) вместо быстрого поиска по индексу (Index Seek), что фатально сказывается на скорости работы системы при больших объемах данных.
Рекомендация: старайтесь избегать избыточных составных типов. Если реквизит может ссылаться только на один конкретный справочник, указывайте в конфигураторе жесткий тип, а не оставляйте составной тип "на всякий случай". Это позволит СУБД создать ровно одно поле ссылки и строить максимально эффективные индексы и планы запросов.