Чем большее количество СУБД и ОС поддерживает какая-либо программа – тем больше у нее пользователей, и это хорошо для производителей программы. При этом нужно помнить, что поддержка каждой СУБД – это расходы на разработку и тестирование, и эти расходы хорошо бы минимизировать.
В этой статье мы расскажем о том, как нам удалось написать технологическую платформу, которая способна без изменения кода бизнес-приложения работать с самыми часто используемыми в бизнесе/организациях СУБД.
О том, как мы работаем без изменения кода бизнес-приложения на разных ОС – тут, с различными браузерами - тут, а как на разных мобильных ОС – здесь.
Для тех, кто не в теме 1С - короткий ликбез о технологической платформе 1С:Предприятие. Платформа 1С:Предприятие – это среда быстрой разработки бизнес-приложений. Мы старались спроектировать платформу так, чтобы разработчик бизнес-приложений (в терминологии 1С - прикладной разработчик) как можно меньше думал о том, на какой ОС и СУБД работает его код, да и в целом меньше думал именно о технических деталях, а сосредоточился на бизнес-задачах (подробнее об этом здесь).
Поэтому прикладной разработчик в 1С:Предприятии работает с прикладными же объектами (справочниками, документами, планами счетов и т.д.), не беспокоясь о том, как обеспечить их хранение в базе данных. Почему мы выбрали такой подход – написано в статье «Как мы в 1С:Предприятии работаем с моделями данных, или Почему мы не работаем с таблицами». А ещё необходимо обеспечить целостность и непротиворечивость данных. Т.к. работа может вестись под разными операционными системами, или у заказчиков могут быть установлены свои СУБД, то необходима поддержка наиболее распространенных СУБД. И данные из одной СУБД должны без проблем переноситься в другую.
Уже упомянутый прикладной разработчик сам создаёт объекты в приложении (конфигурации в терминах 1С:Предприятия):
У многих типов прикладных объектов можно добавлять реквизиты для хранения свойств этих объектов (которые являются отражением свойств объектов из реального мира – например наименования у товара). Наиболее близкий аналог реквизита – поле/свойство класса (property, field) в традиционных ООП-языках.
Платформа же сама создаст для этих объектов таблицы и обеспечит связи между этими таблицами. От прикладного разработчика скрыта вся «магия» конкретной СУБД и особенности синтаксиса её языка. Чаще всего разработка конфигурации ведётся в одной СУБД, а непосредственная работа с ней – в другой. Если конфигурация окажется успешной и будет установлена у большого числа пользователей, она может работать с такими СУБД, о которых прикладной разработчик даже не думал при её создании. Все сложности берет на себя платформа 1С:Предприятие.
Какие СУБД поддерживает 1С:Предпрятие?
В качестве «рабочей» СУБД, где хранятся бизнес-данные – четыре самые распространенные промышленные СУБД:
Microsoft SQL Server
PostgreSQL
Oracle Database
IBM DB2
Также существует собственный формат файловой базы данных 1С, предназначенный для работы небольшого числа пользователей.
Ещё есть Дата акселератор – in-memory СУБД нашей собственной разработки, которая хранит все данные в оперативной памяти (в нее копируются данные из рабочей СУБД) и сильно помогает при выполнении сложных аналитических отчетов, иногда ускоряя их выполнение на порядки.
При необходимости можно обратиться из конфигурации к любой другой СУБД, имеющей ODBC-драйвер, через механизм внешних источников данных.
Посмотрим на примере. Надо реализовать в приложении сущность «Товар» с подчиненной сущностью один-ко-многим «Дополнительные реквизиты». И уметь оперировать со списком товаров.
Наш любимый прикладной разработчик выбрал для реализации требования прикладной объект Справочник, позволяющий хранить данные, имеющие одинаковую структуру и списочный характер. Справочник также позволяет для каждого элемента справочника хранить некоторый набор информации, которая одинакова по своей структуре, но различна по количеству, для разных элементов справочника. Для этого у справочника есть подчиненные сущности – табличные части.
И прикладной разработчик создал справочник Товары с табличной частью ДополнительныеСвойства.
Если загрузить эту конфигурацию на разные СУБД, то видны отличия в наименованиях колонок.
Например, в MS SQL и PostgreSQL используется подчеркивание («_») в качестве первого символа.
В Oracle Database своя специфика:
А в IBM DB2 каждое текстовое поле дополнено ещё одним, с постфиксом «U» - там хранятся строки в верхнем регистре, т.к. IBM DB2 может выполнять только регистрозависимый поиск.
Кроме того, в IBM DB2 при работе с 1С:Предприятием используются не таблицы, а их так называемые алиасы, сами же таблицы имеют достаточно замысловатые имена.
Видите, сколько особенностей необходимо учесть? Но прикладному разработчику требуется не больше минуты, чтобы создать такую структуру в любой из поддерживаемых платформой СУБД. Особо хочется подчеркнуть, что прикладной разработчик не заботится о том, на какой конкретно СУБД будет создана структура для хранения данных и будут выполняться запросы на добавление, извлечение, обновление и удаление информации – всё за него сделает платформа «под капотом». Платформа 1С:Предприятие – не единственный фреймворк, обеспечивающий работу с разными СУБД, но один из немногих, обеспечивающий переносимость приложений между разными СУБД без модификации прикладного кода.
To ORM or not to ORM?
Опытный программист тут же скажет, что есть технология ORM (Object-Relational Mapping), обеспечивающая разработку в терминах классов, а не в терминах таблиц базы данных. И будет прав. Эта технология избавляет разработчика от необходимости писать запросы к базе данных и позволяет манипулировать только объектами. Тем более, что уже есть много готовых реализаций этой технологии. Например, для одной только Java:
…и множество других
Вот так примерно будет выглядеть реализация нашего справочника товаров на Hibernate. Мы создаем объект Товар и объект «Строка таблицы 1». Описываем с помощью аннотаций имена таблиц, для каждого класса – своя таблица. Также указываем, какие поля в каких колонках таблицы хранятся. И не забываем про связи между классами, которые потом превратятся в связи между таблицами.
В случае платформы 1С:Предприятие использовать классический ORM не получится, т.к. ORM компилируется вместе с исходным кодом приложения, разработка же конфигураций идет на встроенном языке 1С.
Как правило ORM поддерживают несколько СУБД. Но разработать с помощью ORM приложение, полностью нейтральное по отношению к типу СУБД, почти нереально. В частности, язык запросов ORM довольно прямолинейно транслируется в SQL. При этом, конечно, учитываются некоторые синтаксические детали диалекта SQL конкретной СУБД, но не более. Образно говоря, через языки запросов ORM "протекают" особенности конкретных СУБД.
И как ещё один аргумент против использования концепции Object-Relational Mapping для наших задач, стоит упомянуть, что прикладные разработчики на 1С пишут свои SQL-подобные запросы для доступа к данным.
Учтя всё вышеперечисленное, мы поняли, что для решения наших задач надо писать свой собственный ORM, и даже нечто большее, чем просто ORM. Что мы, в итоге, и сделали.
Язык запросов SDBL
Мы уже видели, что бывают случаи, когда одно свойство объекта преобразуется в два поля. Но бывают ещё и ситуации, когда один объект может состоять из нескольких таблиц. Например, есть прикладной объект «Регистр накопления», составляющий основу механизма учета движения средств (финансов, товаров, материалов и т. д.), который позволяет автоматизировать такие направления, как складской учет, взаиморасчеты, планирование.
Информация в регистре накопления хранится в виде записей, каждая из которых содержит значения измерений и соответствующие им значения ресурсов. Измерения описывают разрезы, в которых хранится информация, а в ресурсах регистра накапливаются нужные числовые данные. Также каждая запись хранит дату (она заполняется платформой автоматически из текущей даты, но прикладной разработчик может её переопределить).
Типичный пример использования регистра накопления – учет товарных запасов. Такой регистр будет содержать единственное измерение Товары и один ресурс – остаток товара. Можно добавить ещё одно измерение – Склад, и тогда мы сможем анализировать информацию не только по остаткам каждого товара в целом, но и по остаткам товаров на каждом складе.
Регистр накопления автоматически рассчитывает итоги по заданным промежуткам времени, что позволяет быстро составлять отчеты. Для реализации регистра накопления в базе данных создаются три таблицы:
Таблица движений (пришло/ушло в нашем случае товаров)
Таблица настроек хранения итогов регистров накопления (до какого момента рассчитаны итоги)
Таблица итогов по периодам времени
Также необходимо избежать потери данных при изменении объекта в конфигурации (например, добавление новых измерений и/или ресурсов или переименование или изменение типов существующих) и миграции на эту новую, измененную версию конфигурации.
Для решения вышеуказанных проблем нами был разработан специальный язык, который мы так и назвали - Special Database Language (SDBL). Это «промежуточный» язык для работы с данными. Любое взаимодействие с базой данных в платформе 1С:Предприятие происходит только через него – все запросы из встроенного языка 1С, все служебные запросы платформы, любая модификация данных объектов выполняются путем выполнения SDBL-запроса.
Например, прикладной разработчик пишет простой запрос к справочнику товаров для получения наименования и вида товара. Этот запрос транслируется платформой в язык SDBL, а затем запрос SDBL транслируется в запрос к конкретной СУБД:
Здесь – более сложный случай, когда разработчик хочет записать новый объект. Платформа автоматически присвоит объекту новый код (для чего найдет запросом максимальный код в справочнике), а затем выполнит вставку объекта в таблицу.
Как можно видеть, на уровне SDBL мы сразу описываем данные объекта и его табличной части. В итоге один запрос SDBL распадается на несколько запросов к СУБД: сначала – вставка в таблицу товаров, затем – вставка в таблицу единиц измерения, и в конце – проверка версии объекта для обеспечения согласованности данных.
Язык SDBL используется также в исходном коде платформы, что позволяет и в платформе писать СУБД-независимый код. В коде на С++ вся работа с данными идет только через SDBL. Например, мы хотим установить прикладному объекту новый код. Для этого в коде платформы формируется текст SDBL-запроса для поиска максимального существующего кода. Затем выполняется запуск этого запроса, который исполняется уже на конкретной СУБД:
А вот как установка нового кода работает на разных СУБД:
Синтаксис самого SQL – стандартный, но в случае каждой СУБД есть нюансы, и чем запрос сложнее – тем их больше.
Давайте посмотрим внутрь. Единицей исполнения SDBL является пакет. Пакет содержит один или несколько операторов SDBL. Существуют два вида пакетов:
Определения данных
Управления данными
Пакет определения данных
Пакет определения данных может содержать операторы:
CREATE
DROP
RENAME
REMOVE
SET_GENERATION
GET_NGENERATIONS
RESTORE
Например, нам нужно создать два справочника – Организации и Склады.
Для этого будет сформирован пакет SDBL, в котором описывается структура новых объектов и их индексов. Остальные операторы относятся к механизму реструктуризации. Механизм реструктуризации предназначен для миграции на новую версию конфигурации, если новая версия содержит изменения структуры прикладных объектов, и, как следствие, изменение структуры базы данных, подробнее про него будет ниже по тексту.
Пакет управления данными
Пакет управления данными может содержать операторы
SELECT
INSERT
DELETE
UPDATE
SET
LOCK
UNLOCK
DROP
Пример запроса 1С:
SQL-запрос для MS SQL Server:
Трансляция и оптимизация
Любое взаимодействие платформы с базой данных превращается в пакет SDBL. Затем этот пакет транслируется в диалект SQL для конкретной СУБД.
Под понятием трансляции пакета SDBL в диалект SQL скрыт большой и сложный механизм. Помимо собственно трансляции происходит оптимизация SDBL-запроса, добавление ограничений доступа для пользователя на уровне записей, если такие ограничения определены в конфигурации (Row-Level Security, RLS) и много другого. В конечном случае получается, что если нам необходимо поддержать ещё одну СУБД, то достаточно написать транслятор SDBL-запроса в диалект SQL этой СУБД.
Ещё одно преимущество SDBL – быстрая оптимизация запросов для каждой поддерживаемой СУБД. На примере ниже – реальный случай, когда служебный запрос платформы к остаткам регистра накопления (порожденный запросом из конкретной конфигурации) был неоптимальным на MS SQL. Неоптимальность заключалась в использовании оператора ИЛИ (OR) в секции условия отбора запроса.
Запрос и его план на тестовой базе выглядел так:
В итоге для того, чтобы выбрать около 2000 строк, запросу приходилось читать с диска почти 700 000 строк. Для оптимизации этого запроса было изменено условие отбора – оператор ИЛИ был заменен на операторы И НЕ.
Условие отбора был изменено внутри платформы в SDBL-запросе, таким образом, эта оптимизация будет применена для всех поддерживаемых СУБД.
Как мы видим – количество прочтенных строк сократилось более чем в 10 раз:
Реструктуризация данных
Прикладные разработчики могут в ходе разработки новых версий конфигураций менять структуру существующих объектов, добавлять новые объекты, удалять старые. При миграции на новую версию конфигурации некоторые такие изменения могут привести к нарушению целостности данных или даже к потере данных. Чтобы избежать таких проблем существует механизм реструктуризации, который также работает через SDBL.
Работает механизм реструктуризации примерно так:
Давайте попробуем разобраться в этом нагромождении окошек и стрелочек.
Итак, у товара (точнее, у прикладного объекта-справочника Товар) есть реквизит ВладелецТовара. Серым цветом выделено его текущее состояние. Этот реквизит имеет составной тип данных и может принимать значение типа Организация или Склад. В схеме SDBL он описан как реквизит составного типа.
В ходе разработки новой версии было принято решение что владельцем товара может быть только организация. Прикладной разработчик изменил тип реквизита. Теперь реквизит ВладелецТовара – не составной и ссылается только на справочник Организации.
После нажатия кнопки «Обновить конфигурацию базы данных» создается новое поколение таблицы товаров с помощью оператора SDBL “CREATE NEW GENERATION”, где реквизит ВладелецТовара уже не составной и является ссылкой на таблицу организаций. При этом в СУБД «лишние» колонки, которые предназначались для составного типа, помечаются постфиксами «_OO». Пользователю выдается предупреждение об изменении структуры справочника Товары. Если владельцем хотя бы одного товара был склад, то пользователь увидел бы ошибку, сообщающую, что реструктуризация не может быть выполнена, так как это приведет к потере данных, и изменения структуры данных не могут быть применены. Такое поведение платформы – защита от потери данных. В такой ситуации пользователь может найти все товары, владельцем которых является склад, и сознательно заменить владельца на нужную организацию. Но в целом при разработке конфигураций мы стараемся избегать таких изменений.
А в случае отказа от принятия изменений механизм реструктуризации выполнит команду SDBL “ROLLBACK” и вернет конфигурацию базы данных в прежнее состояние.
Если же изменения будут приняты, как показано на иллюстрации выше, то будет выполнен оператор «COMMIT» и база данных переключится на новое поколение, т.е. будут удалены «лишние» колонки таблицы товаров и реквизит ВладелецТовара изменит свой тип.
Внешние источники данных
А ещё через механизм внешних источников данных в конфигурации можно получить данные из любой ODBC-совместимой СУБД. Например, есть таблица работников в СУБД SQLite:
Для доступа к ней из 1С создаём внешний источник данных, пишем строку подключения к базе, выбираем таблицы и поля, нужные нам, и с помощью языка запросов 1С:Предприятия читаем данные:
Эта функциональность также работает через SDBL по знакомой нам схеме. Сначала запрос 1С преобразуется в SDBL, а затем – в запрос к конкретной используемой СУБД.
Транзакции в СУБД
Отдельно стоит упомянуть про такую большую тему, как блокировки данных и уровни изоляции транзакций. Каждая СУБД в этом плане ведёт себя по-разному. Где-то конкурентный доступ к данным управляется на уровне блокировок, когда одна транзакция блокирует работу с данными и другие транзакции в это время не могут работать с этими данными. В других СУБД используется версионный режим, когда перед началом транзакции делается копия данных, и другие транзакции получают данные из этого снимка, пока исходная транзакция не завершится.
Платформа 1С:Предприятие может использовать собственные так называемые управляемые блокировки данных.
Однако с точки зрения прикладного разработчика (да и разработчиков платформы) всё прозрачно – пишутся запросы (прикладным разработчиком на языке запросов 1С, разработчиком платформы – на SDBL). Ну а в случае необходимости у прикладного есть возможность самому управлять блокировками данных в тех случаях, когда бизнес-логика требует согласованного и целостного чтения данных в транзакции.
А транслятор для конкретной СУБД сам понимает, как управлять целостностью данных в транзакции.
Комментарии (66)
NitroJunkie
09.08.2023 08:32Давно не следил за новыми версиями 1С, но не удержусь не спросить.
Появился ли в оптимизаторе Predicate Push Down? Потому как в PostgreSQL он до сих пор не появился и не совсем понятно нужно ли разработчику когда он пишет запрос проталкивать условие запроса внутрь подзапроса.
Встроили ли запросы в язык? Для resolve'инга, подсветки ошибок, синтаксиса и вот этого всего.
Поддержка оконных функций и рекурсивных CTE в запросах для работы с порядками и рекурсиями (без них делать скажем упорядочивание редкий ад)? И опять-таки поддерживается ли там predicate push down.
Появились ли DML запросы? Или какой-то другой механизм для группового изменения данных?
Появилась ли нормальная поддержка версионного режима, в смысле корректный прозрачный откат и перестарт транзакции, чтобы не заниматься ручными блокировками ?
Возможность разработчику изменять самому физическую модель, материализовать показатели и т.п.?
Поддержка наследования и полиморфизма в запросах?
PeterG Автор
09.08.2023 08:32>"4. Появились ли DML запросы? Или какой-то другой механизм для группового изменения данных?"
Не появились, и это на данный момент наша принципиальная позиция.
NitroJunkie
09.08.2023 08:32А чем эта позиция обусловлена? В том плане, что проблема N+1 есть же не только при чтении, но и при записи? Не говоря уже о том, что при использовании механизма "триггеров" (событий) проблема N+1 по сути переносится с записи назад на чтение (когда цепочка триггеров может порождать цепочку запросов на чтение, если в этих триггерах есть бизнес-логика).
То есть проблема техническая или логическая? Потому как с логической точки зрения всякие огромные документы инвентаризации, генерации дисконтных карт никто же не отменял.
PeterG Автор
09.08.2023 08:32Технической или логической проблемы я тут не вижу.
Мы не реализуем механизм для группового изменения данных потому, что это "Веревка достаточной длины, чтобы… выстрелить себе в ногу".
Коротко говоря - слишком мощный механизм при достаточно слабом его контроле. Можно будет, например, одной командой случайно удалить все заказы в системе. Что не очень хорошо.
>"всякие огромные документы инвентаризации "
Документ целиком как раз поменять/удалить можно.
А вот набор документов одной командой - нет.NitroJunkie
09.08.2023 08:32+3Ну у вас с платформой то разработчики работают, а не пользователи. Они если надо в for'е себе в ногу выстрелят (даже с большей легкостью). И со слабым контролем непонятно, так как как раз именно при групповом изменении обычно все идет одной транзакцией и она скорее всего тупо не успеет закончится (до того как ее снимут), так как обычно нужно много логики пересчитать / проверить, да и блокировок будет много (ну или update conflict'ов в версионном режиме).
Документ целиком как раз поменять/удалить можно.
Ну вот это как раз и странно. В тех же типовых со строками документа работа идет через времянки, то есть запросами (для производительности), а с другими объектами такой возможности нет (хотя справочник штука абстрактная, и может быть очень большим, больше чем строк в документах).
PeterG Автор
09.08.2023 08:32>"Ну у вас с платформой то разработчики работают, а не пользователи. Они если надо в for'е себе в ногу выстрелят (даже с большей легкостью)."
Тут не могу с вами согласиться исходя из личного опыта.
"DELETE FROM ..." написать гораздо проще и быстрее, особенно в консоли запросов (и случайно нажать шорткат для "выполнить" и потом кусать локти), чем в коде написать в for'е, а код потом надо ещё и запустить на исполнение - тут у программиста больше времени остается на "одуматься".
Ndochp
09.08.2023 08:32+2Вот записать пустой набор без отбора в регистре сведений [и очистить его этим в ноль] можно, а массово заменить контрагента Пупкина на Дупкина нет. И это грустно (обе причем ситуации)
PeterG Автор
09.08.2023 08:32>"7. Поддержка наследования и полиморфизма в запросах?"
Если мы про язык запросов - в нем поддерживается ровно столько же наследования и полиморфизма, сколько поддерживается в языке SQL.
Возможно, я не понял ваш вопрос - можете пояснить на примерах?NitroJunkie
09.08.2023 08:32Я имел ввиду, что можно задать класс скажем ДокументРасхода. Дальше наследовать скажем ДокументСписания от ДокументРасхода, ДокументПродажи от ДокументРасхода, после чего делать скажем SELECT SUM(что-то там) FROM ДокументРасхода GROUP BY что-то там, и платформа сама построит запрос с UNION'ами всякими и т.п. Наследование таблиц называется в некоторых СУБД (хотя там скажем прямо не настолько удобная вещь по ряду причин).
PeterG Автор
09.08.2023 08:32Такого у нас пока нет.
Не в последнюю очередь потому, что не у всех поддерживаемых нами СУБД есть такая фича.
PeterG Автор
09.08.2023 08:32>"5. Появилась ли нормальная поддержка версионного режима, в смысле корректный прозрачный откат и перестарт транзакции, чтобы не заниматься ручными блокировками ?"
"Нормальная" - это от слова "норма".
Можете пожалуйста пояснить (лучше на конкретном примере), что вы считаете нормой для поддержки версионного режима?NitroJunkie
09.08.2023 08:32+3Смотрите.
Как можно автоматически обеспечить целостность.
Первый вариант - блокировать все читаемые данные (в 1С как я понимаю это называются автоматические блокировки). Вариант с одной стороны хороший, но не масштабируемый. Какая нибудь долгая операция записи может ввести базу в клинч (чтение тоже, но это частично можно решить переведя такие операции в версионный режим, в MS SQL есть такой мутант)
Второй вариант - мультиверсионность. Хранить версии записей, читать записи на момент начала транзакции, а при выполнении UPDATE (и COMMIT) следить за версиями записей и если версия устарела - кидать UPDATE CONFLICT (в случае UPDATE, или ошибку сериализации в случае COMMIT). После чего клиент (сервер приложений) должен повторить транзакцию заново (желательно пользователя при этом не уведомлять, так как это техническая фишка). Там могут быть нюансы обеспечения целостности нематериализованных данных в определенных случаях (хотя даже Postgres заявляет что поддерживает true serializable и эту проблему решают), но для решения их достаточно просто иметь прозрачные механизмы материализации.
Собственно второй вариант сейчас по сути в мире считается "золотым стандартом" обеспечения целостности и масштабируемости одновременно. Перекладывать же эту задачу на разработчика это конечно жестоко по отношению к нему.
Ivan22
09.08.2023 08:32это пессимистичный и оптимистичный режимы блокировок
NitroJunkie
09.08.2023 08:32Именно. Хотел отредактировать комментарий и добавить это пояснение, но уже нельзя было его редактировать.
PeterG Автор
09.08.2023 08:32>"6. Возможность разработчику изменять самому физическую модель, материализовать показатели и т.п.?"
Боюсь что не понял вопроса."изменять самому физическую модель, материализовать показатели" - это как?
Можете пояснить на примерах пожалуйста?NitroJunkie
09.08.2023 08:32Ну напримере как я выше кидал. Например если я не хочу UNION'а хочу чтобы была одна таблица, сказать для ДокументРасхода вот эти вот поля храни в такой-то таблице (желательно чтобы можно было задать имена, чтобы потом можно было подключаться напрямую к базе и читать данные из этих таблиц, хотя это скорее бонус).
PeterG Автор
09.08.2023 08:32То, что вы описали, напоминает мне функкциональность VIEW.
Это оно или я ошибаюсь?
И - такой функциональности у нас пока нет.NitroJunkie
09.08.2023 08:32Ну VIEW это все же не совсем то. Хотя может использоваться формально как workaround, но проблема что подход работы через VIEW не "модульный" (грубо говоря ты наследуешь классы не в месте объявления конкретного класса, а в месте объявления абстрактного класса ).
redtram
09.08.2023 08:32Складывается впечатление, что то о чем Вы говорите реализовано во встроенном языке/модели. Абстрактные классы - Определямый тип, что в свою очередь тоже самое, что и составной тип. При запросе к свойству экземпляра (к реквизиту через разыменовывание полей через точку) SDBL сам превратит всё в JOIN-ы ко всем таблицами, в которых может храниться. Если не хочется JOIN ко всем таблицам, то можно сделать предварительный CAST встроенным языком запросов. Такой вот Workaround.
А если очень хочется хранить что-то из свойств в отдельной таблице (для ускорения чтения/записи), то платформа позволяет сделать "Регистр сведений" (но придется немного описать логику записи в эту отдельную таблицу), который можно связать по "типизированому" UUID с основной таблицей.
PeterG Автор
09.08.2023 08:32>"2. Встроили ли запросы в язык? Для resolve'инга, подсветки ошибок, синтаксиса и вот этого всего."
Можно сказать что да - в среде разработки 1C:Enterprise Development Tools для этого много сделано.NitroJunkie
09.08.2023 08:32То есть вы оставили в строковых литералах и сделали что-то типа language injection в IDEA? Круто конечно, только я не понимаю как это может теоретически работать в императивном коде, где запрос может склеиваться в зависимости от услови, скажем, и заведомо не знаешь в данном литерале весь запрос или его кусок (тогда что ошибка светится?). То есть не встраивая синтаксис запросов в язык, как .NET (C#) это сделал с LINQ, не совсем понятно как это все можно парсить / анализировать.
EvilBeaver
09.08.2023 08:32Короткий ответ "никак". Более длинный - запросы в 1С далеко не всегда клеят из частей. 70-80% запросов это цельные литералы. Разумеется, есть и сборка запросов конкатенацией, тогда каких-то прекрасных подсказок не будет.
NitroJunkie
09.08.2023 08:32Так в этом и вопрос, как платформа определеяет, если я условно напишу:
a = "SELECT a, b,"
a += "c FROM g" ;
Первый это полный запрос или нет. То есть нужно ли там синтаксическую / семантическую ошибку светить?
PeterG Автор
09.08.2023 08:32>"3. Поддержка оконных функций и рекурсивных CTE в запросах для работы с порядками и рекурсиями "
Такого пока нет, но запросы на эту функциональность есть.
Возьмемся реализовывать - напишем на Зазеркалье с указанием планируемой версии платформы.
CrushBy
09.08.2023 08:32+1Я не очень понял. То есть при каждом формировании запроса, вы собираете строки по определенной грамматике (SDBL), а потом на каком-то уровне обратно разбираете из строк во внутренние структуры, а затем назад собираете уже в SQL нужного синтаксиса ? Нет ли при этом большого overhead'а (все-таки это частая операция) ? И зачем для этого была целая грамматика вместо использования просто какой-то внутренней структуры (классов) без языка ?
PeterG Автор
09.08.2023 08:32+1При выполнении запроса, написанного на встроенном языке, в общем случае запрос парсится 3 раза:
Исходный запрос из конфигурации
Запрос SDBL
-
Запрос SQL (движком СУБД)
Парсинг запросов: Язык запросов - SDBL - SQL привносит дополнительные затраты, но они не значительны. С другой стороны, формулирование запросов в виде текста, а не в виде двоичных данных сложной структуры, значительно экономит время процессора и усилия разработчиков на их написание - или программное построение. Запросы бывают весьма сложные. Поэтому и было принято решение о текстовых запросах. Единственным исключением является участие в запросах длинных двоичных данных, для задания которых используется параметр в тексте запроса и отдельное двоичное значение с данными.
CrushBy
09.08.2023 08:32Парсинг запросов: Язык запросов - SDBL - SQL привносит дополнительные затраты, но они не значительны. С другой стороны, формулирование запросов в виде текста, а не в виде двоичных данных сложной структуры, значительно экономит время процессора и услилия разработчиков на их написание - или программное построение
По-моему, это противоречивые утверждения.
И не очень понимаю про усилия разработчиков по их написанию. Речь идет о разработчиках платформы (ведь только они работают с SDBL) ? Если так, то они с точки зрения архитектуры же не со строками работают же (хотя конечно можно все replace'ами делать, но это странно). Например, у вас там есть класс SDBLCommand. Чем удобнее делать cmd.append("WHERE ..."), чем cmd.addWhere(...), cmd.addColumn(...) ? Причем передавать туда не строки, а уже ссылки на конкретные объекты, отвечающие за колонки, и т.д...
DMGarikk
09.08.2023 08:32Чем удобнее делать cmd.append("WHERE ..."),
… помню код в типовых конфигурациях 1С где такое сплошь и рядом… я догадываюсь что там у них один архитектор с такой точкой зрения
Ndochp
09.08.2023 08:32Там и объект "Схема" есть, который объектное представление запроса. Поработав с ней с радостью возвращаешься к замене строк.
Egomachine
09.08.2023 08:32Всегда было интересно, почему не реализовали поддержку Firebird SQL? Интересная же СУБД. И журнал регистрации на ней же существенно интереснее SQLite смотрится. Есть emedded- варианты, есть полноценные клиент-серверные, ЖР вообще можно в таком случае на другой сервер вынести с минимальными затратами.
PeterG Автор
09.08.2023 08:32>"почему не реализовали поддержку Firebird SQL? "
В смысле - как "рабочей" СУБД где хранятся бизнес-данные?
Наряду с MS SQL, PostgreSQL, Oracle и IBM DB2 ?Egomachine
09.08.2023 08:32Да, как рабочей СУБД для бизнес-данных. Понятно, что у Firebird прошлых версий были достаточно существенные ограничения по реструктуризации объектов базы, это могло быть для вас существенным ограничивающим фактором - если я правильно помню, до 256 раз (1 байтовый счётчик) можно было изменять структуру таблицы, до сброса счётчиков. Но насколько мне известно, у последних версий Firebird эти ограничения уже существенно расширены, не должны уже быть препятствием.
PeterG Автор
09.08.2023 08:32По нашей информации - Firebird в бизнесе используется не так часто, как MS SQL или PostgreSQL например. А поддержка ещё одной СУБД - это серьезные расходы на разработку, тестирование и поддержку.
Egomachine
09.08.2023 08:32Я бы не согласился с таким утверждением. Вы же понимаете, что определяющим фактором является широкое использование 1С в бизнесе. Раз уже есть 1С и к ней уже прилагается [относительно честно] купленный MS SQL Server - почему бы и не использовать эту РСУБД и в других бизнес-приложениях. Я даже предположу, что Firebird мог бы поспорить по количеству установок с MS SQL, если бы поддерживался платформой 1С. Он свободный, кроссплатформенный, лёгкий, проще в обслуживании, мощный и к тому же - чистопородный версионник.
В бизнесе я часто с ним сталкивался - достаточно широко используется в отраслевых бизнес-приложения, не говоря уж о том, что я сам разработал на заказ более десятка приложений на основе Firebird.
PeterG Автор
09.08.2023 08:32Ну видите - у каждого свой опыт и своя статистика использования БД.
Я в частности работаю со сбором пожеланий по развитию платформы - пока не встречал запросов по поддержке Firebird (хотя сам лично с ней когда-то работал, когда она называлась Interbase).
Кстати о пожеланиях по развитию платформы - мы их собираем в Телеграме, каждую пятницу собранные тут предложения приезжают ко мне и после уточнения деталей помещаются в нашу базу.Egomachine
09.08.2023 08:32Верно, ранее называлась Interbase. Но с тех пор многое изменилось и эта СУБД не только получила новое имя Firebird, но и сильно выросла.
А можете записать здесь пожелание добавить поддержку Firebird ? Ну, например, в качестве БД для журнала регистрации хотя бы ? Существует embedded-вариант Firebird в виде обычной DLL, но в ней реализован полноценный сервер, только интегрированный в процесс приложения. При возможности указывать путь к файлу базы ЖР легко и тривиально переносится на клиент-серверный вариант Firebird на другом хосте - всего лишь добавлением имени хоста в путь к БД журнала.
А там глядишь, и оцените возможности добавить Firebird в список БД для бизнес-данных :-)
starfair
09.08.2023 08:32В случае платформы 1С:Предприятие использовать классический ORM не получится, т.к. ORM компилируется вместе с исходным кодом приложения, разработка же конфигураций идет на встроенном языке 1С.
Очень "правильный" ответ! А если это ваш собственный язык. то что мешает вам в него интегрировать свою же версию ORM? Зачем городить ещё одну дополнительную сущность в программах, которую надо программистам-прикладникам изучать (ну это ладно!) и поддерживать (а это плохо, ибо специфические надстройки это значит специфический специалист по ним, а для бизнеса это опасно)?
Darth_Malok
09.08.2023 08:32+1Я не настоящий программист, но разве объектная модель работы с данными (Справочники.Справочник.СоздатьЭлемент()) — это не ORM?
PeterG Автор
09.08.2023 08:32+1В известном смысле - да, ORM.
Процитирую статью:
"Учтя всё вышеперечисленное, мы поняли, что для решения наших задач надо писать свой собственный ORM, и даже нечто большее, чем просто ORM. Что мы, в итоге, и сделали."
PeterG Автор
09.08.2023 08:32>"Зачем городить ещё одну дополнительную сущность в программах "
А что вы в данном случае называете дополнительной сущностью?starfair
09.08.2023 08:32Дополнительную прослойку запросов к БД. Почему нельзя делать это сразу из скриптов 1С?
PeterG Автор
09.08.2023 08:32Если я вас правильно понял - вы предлагаете писать SQL-запросы непосредственно в коде 1С?
Поправьте меня если ошибаюсь.starfair
09.08.2023 08:32Именно. По крайней мере, иметь такую возможность для тех, кто разбирается в том, как это делается грамотно. Я не сомневаюсь в компетенциях ваших специалистов, но то, что у вас между запросом пользователя и конечным запросом к БД несколько ступеней, скорее всего делает вашу схему во первых более уязвимой к ошибкам на любом из этапов, во вторых - однозначно менее гибкой, так как выходит, что изначальный запрос это априори более маленькое подмножество от изначальных возможностей стандарта SQL, не говоря уже об особых нюансах в каждой из реализаций конкретных СУБД
PeterG Автор
09.08.2023 08:32Недостатки - это продолжение достоинств (с).
Наличие прослойки в виде SDBL делает механизм менее гибким, согласен.
Но зато позволяет, в частности:
- Накладывать на запрос условия RLS (Row-Level Security)
- Оптимизировать запросы на уровне платформы (один из примеров оптимизации описан в статье)
- Адаптировать запрос для диалекта SQL конкретной СУБД (как опять же описано в статье)
Стандарт SQL - его даже флагманы индустрии СУБД поддерживают очень по разному - и в синтаксисе, и в реализации. Что-то сложнее "SELECT * FROM ..." уже начинает сильно разниться на MS SQL и Oracle например.starfair
09.08.2023 08:32Ещё раз, уточню. Вопрос не в том, что ваш инструмент плох. А в том, что вы не оставляете альтернатив, кроме его использования. Работа с данными предусматривает много разных сценариев, и может быть вы конечно предусмотрели в своей прослойки основные типовые, но далеко не факт, что не найдётся то, что вашим скриптовым языком не решается, а из альтернатив останется писание в админке СУБД своих запросов, с последующим придумыванием как это всё потом импортировать обратно в 1С
DMGarikk
09.08.2023 08:32А в том, что вы не оставляете альтернатив, кроме его использования.
писание в админке СУБД своих запросов, с последующим придумыванием как это всё потом импортировать обратно в 1Св лиц.соглашении еще есть прямой запрет на работу с базой в обход штатных средств платформы
PeterG Автор
09.08.2023 08:32Понимаю вас.
Принимая такое архитектурное решение, мы взвесили все "за" и "против", посмотрели на аналогичные продукты а рынке и на архитектурные решения, принятые в них.
Из своего личного опыта разработки (до-1Сного) скажу, что наличие прямых SQL-запросов в коде прикладного решения влечет за собой массу проблем (в частности, с производительностью) даже при переезде с одной версии СУБД на другую. И централизованно эти проблемы решить бывает очень накладно, т.к. их нужно решать в куче мест в прикладном коде. Приходится делать одно "бутылочное горлышко", через которое прикладной код обращается к СУБД, и пытаться оптимизировать запросы в этом "бутылочном горлышке". Ну а отсюда уже один шаг до SDBL.DMGarikk
09.08.2023 08:32ну вот кстати по поводу переезда между СУБД
Уже много много лет существует негласное мнение что 1С нормально работает только на MSSQL, на постгри с определенными приседаниями, а смельчаков которые решаются запускать это всё на оракле или db2 так вообще единицы и у них это получается так себе
причем проблемы с совместимостью БД были еще и до восьмерки, в 7.7 были совершенно однозначные различия между файловой и sql версиями, хотя платформа "какбы" должна была скрывать такое
ну и вот сейчас 23 год, а я до сих пор слышу настоятельные рекомендации юзать mssql, несмотря на то что постгри уже достаточно стабильна в поддержке
NitroJunkie
09.08.2023 08:32У PostgreSQL как минимум: нет predicate push down, очень ограниченная работа с cross-column статистикой и "утечки" памяти при активном использовании временных таблиц / сложных запросов (возможно это не утечки, а просто очень большой memory footprint, но сути дела это не меняет). Все это требует достаточно много оптимизаторов со стороны платформы (тем самым выполняя работу СУБД), чтобы сложное решение на нормальных объемах нормально работало на PostgreSQL.
Хотя конечно если у вас решение или очень простое или в нем мало данных (то есть грубо говоря < 100k активно используемых записей в таблице) или сделано императивно спагетти-кодом то может и на PostgreSQL взлететь. Но "стабильность в поддержке" тут не при чем.
Вот тут немного подробней описано: https://habr.com/ru/companies/lsfusion/articles/463095
starfair
09.08.2023 08:32Вы знаете, вот чем больше в последнее время занимаюсь вынужденно вопросами отечественного ПО прикладного порядка, тем больше прихожу к выводу, что наши крупные разработчики просто забили огромный болт, и считают что всё знают заранее лучше конечных пользователей своих продуктов. Никто и не говорит о том, что риски есть, проблема начинается тогда, когда без рисков этих выкрутиться не получается. Вот тогда и плодятся горе программисты, которые вместо основного разработчика начинают на местах делать костыли, раз уж нет штатных методов решения в самом ПО! Вы мне напоминаете всемирно известную компанию из Купертино, в продуктах которой вообще ничего нельзя, чего бы не разрешил "святой" Стив
80lvlAPP
09.08.2023 08:32Спасибо за статью, хотелось бы отметить что отсутствие возможности выполнять операции по записи по списку объектов очень ограничивают применимость платформы для заказчиков с большими объемами и приводит к запросам на запись в цикле.
vadim_bv
09.08.2023 08:32Например, мы хотим установить прикладному объекту новый код. Для этого в коде платформы формируется текст SDBL-запроса для поиска максимального существующего кода. Затем выполняется запуск этого запроса, который исполняется уже на конкретной СУБД:
[картинка]
А вот как установка нового кода работает на разных СУБД:
Синтаксис самого SQL – стандартный, но в случае каждой СУБД есть нюансы, и чем запрос сложнее – тем их больше.Вот здесь не понял картинки. Если нужно установить новый код, то делается select max(code) из таблицы? Что там с изолированностью транзакций? Почему не используются последовательности?
Dddn
09.08.2023 08:32Когда читаешь про 1С, всё круто. А как только установишь ERP и начнешь работать, обязательно вылезет один из тысячи багов.
vvrvvr
Спасибо за действительно интересную статью, коллеги!
PeterG Автор
Всегда пожалуйста!
vvrvvr
Петр, планируется ли в ближайшее время расширение списка поддерживаемых СУБД?
PeterG Автор
В ближайшее время - насколько знаю, таких планов нет.
По моей личной статистике - бльше всего пока запросов на поддержку MySQL.
Но моя личная статистика не влияет на планы развития платформы :)
Вот тут ниже в каментах просят например Firebird, меня про него ранее никогда не справшивали.
Как говорится - следите за новостями!
Публичные планы мы размещаем на Зазеркалье.
Вот например предварительный план на версию 8.3.26