Регистры сведений. История одного «велосипеда»

9f378584a809bcea2260765012f44448.png

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

Один из вопросов звучал безобидно и просто: «Что такое регистр сведений?» Тем удивительнее было наблюдать, как многие буквально спотыкались об него. Впечатление было такое, как будто человек шел, шел и не заметил стеклянную дверь. Тогда-то я впервые задумался о том, что не так с этим изобретением.

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

Но все это произошло до появления восьмой версии, да и в общем-то и до появления 1С как таковой. Разработчики восьмерки, стремясь всенепременно изобрести что-нибудь свое, выделили в отдельный класс то, что в сущности является всего-лишь одной из возможных опций таблицы (или справочника, если вам так удобнее называть). Всякая запись (кортеж) служит для отображения связей между сущностями. Но характер этих связей может быть разным. Иногда это одна основная сущность и множество сущностей, подчиненных основной. Например, основная сущность «товар», а «наименование», «модель», «размер», «цвет» — это все сущности-атрибуты, подчиненные основной. Еще пример: «контрагент», как основная сущность, и «наименование», «адрес», «телефон», как подчиненные.

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

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

Создатели платформы 1С: Предприятие 8 могли бы просто дать возможность указывать явным образом первичный ключ в справочнике, но они пошли по пути «велосипедостроительства» и создали отдельный класс для таблиц с составным первичным ключом, назвав его регистром сведений. Тут можно возразить, что я только что хвалил создание новых классов. Наличие поля типа «дата» тоже, по сути, опция. Почему же тогда нельзя создать новый класс по опции составного первичного ключа? Тут все дело в результате. «Справочники» и «документы» понятны всем практически «с лету». Это я вам, как преподаватель с двадцатилетним стажем, могу со всей ответственностью заявить. А вот «регистры» окутаны туманом. А «регистры сведений» непроницаемым туманом. Нет, человек с некоторым уровнем развития всяко может догадаться, что «регистр сведений» — это некое место, где хранятся «сведения». Но его следующим, законным вопросом будет:, а что мы тогда храним в «документах» и «справочниках»? Не «сведения»? А что?

А все дело в том, что «регистры» уже существовали в предыдущей версии. Такое ощущение, что разработчики восьмерки, отважившись на создание нового класса, ровно на этом месте исчерпали весь запас своей креативности. Новый класс они зачем-то отнесли к семейству «регистров», переименовав тру-регистры в «регистры накопления», а неофитов назвав «регистрами сведений». Более того. У тру-регистров (у регистров накопления) поля разделены на «измерения» и «ресурсы». Что по сути есть поля группировки и поля агрегирования (суммирования). У регистров сведений не стали придумывать ничего нового (конечно! зачем плодить сущности без нужды! ха-ха!) и также разделили поля на «измерения» и «ресурсы». Только здесь «измерения» — это и есть составной уникальный ключ, а «ресурсы» это все прочее. Так в одном случае набор «измерений» уникален и в этом его предназначение, а в другом наборы «измерений» повторяются и в этом тоже заключается предназначение, но уже другое. Одни «ресурсы» суммируются и вообще похожи на ресурсы реального мира, которые могут накапливаться, а могут исчерпываться. А другие «ресурсы»… ну, мы просто экономим слова, говорят нам разработчики. Кажется, если бы мы решили все запутать, у нас вряд ли получилось бы лучше, чем у них. Но и это еще не все.

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

01.01.2019, ООО Ромашка, Ложка, 150 р.

01.02.2019, ООО Ромашка, Вилка, 120 р.

01.03.2019, ООО Ромашка, Вилка, 125 р.

01.02.2020, ООО Незабудка, Ложка, 165 р.

01.03.2020, ООО Незабудка, Ложка, 167 р.

01.08.2021, ООО Василек, Ложка, 190 р.

01.08.2021, ООО Василек, Вилка, 155 р.

01.08.2021, ООО Одуванчик, Ложка, 191 р.

Тогда последние цены конкретных товаров у конкретных поставщиков будут следующие:

01.01.2019, ООО Ромашка, Ложка, 150 р.

01.03.2019, ООО Ромашка, Вилка, 125 р.

01.03.2020, ООО Незабудка, Ложка, 167 р.

01.08.2021, ООО Василек, Ложка, 190 р.

01.08.2021, ООО Василек, Вилка, 155 р.

01.08.2021, ООО Одуванчик, Ложка, 191 р.

А последние цены просто товаров, без учета поставщиков:

01.08.2021, Ложка, 190 р.

01.08.2021, Вилка, 155 р.

01.08.2021, Ложка, 191 р.

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

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

Идея разработчиков заключалась в том, что последние записи должны быть уникальными. И надо сказать, что они верно поняли задачу. Но лучше бы они ее не решали. Если верить Эйнштейну (а у нас нет оснований ему не верить, раз мы пользуемся его формулами практически каждый день), то в реальном мире никакие два события не происходят в точности одновременно. Поэтому, имея поле типа «Дата», которое отмечает точку на временной оси, мы уже имеем уникальность. Достаточно позаботиться о том, чтобы ваша учетная система соответствовала реальному миру в фундаментальных аспектах (а время это именно такой аспект) и создать механизм обеспечивающий уникальность значения типа «Дата» как минимум в рамках информационной базы. Тогда наш результат из вышеприведенного примера естественным образом избавился бы от дублирующих значений:

01.08.2021 00:00:00 78364732678365738465734, Вилка, 155 р.

01.08.2021,00:00:00 78364732678365753438478, Ложка, 191 р.

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

01.08.2021, Ложка, 190 р.

01.08.2021, Вилка, 155 р.

01.08.2021, Ложка, 191 р.

Но это внезапно ломало концепцию уникальности последних записей. Можно было бы вызывать исключительную ситуацию при попытке построить запрос к регистру сведений. И, повторюсь, все-таки лучшим решением было обеспечение уникальности значений типа «Дата». Что же сделали разработчики, столкнувшись с очередной трудностью? А ничего! Вот просто ничего. В текущей реализации запрос к регистру сведений по неполному набору «измерений» вернет:

01.01.2019, Ложка, 150 р.

01.03.2019, Вилка, 125 р.

01.03.2020, Ложка, 167 р.

01.08.2021, Ложка, 190 р.

01.08.2021, Вилка, 155 р.

01.08.2021, Ложка, 191 р.

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

На самом деле все это не так смешно, как я здесь описываю. Потому что в реальности происходит следующее. Вы сталкиваетесь с задачей получения последних записей по неполному набору «измерений». Открываете документацию. В описании виртуальной таблицы среза последних читаете:

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

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

В какой мере были знакомы архитекторы будущей 1С: Предприятие 8 с теорией баз данных мы теперь наверное и не узнаем. Я допускаю крайний вариант — ни в какой. Это было время пионеров. Можно было предложить практически что угодно. В том числе и очевидно нелепые решения. И это проходило, потому что пользователям было трудно дать оценку нового для них продукта, а конкуренты остались где-то далеко позади. Нам же остается утешать себя тем, что зато сегодня мы имеем очень хороший пример очень плохой архитектуры.

© Habrahabr.ru