Разработка на платформе 1С:Предприятие часто вызывает вопросы у специалистов, привыкших к классическим СУБД. Существует мнение, что язык запросов 1С сильно ограничен и не позволяет эффективно решать задачи, которые в стандартном SQL реализуются через коррелированные подзапросы или прямые обновления (UPDATE). Однако платформа активно развивается, и современные версии (8.3.24 и выше) предлагают инструменты, которые кардинально меняют подход к написанию кода. В этой статье мы подробно разберем, как реализовывать сложные выборки, использовать возможности оконных функций и выполнять массовые изменения данных без обращения к тяжелой объектной модели.
Одной из наиболее обсуждаемых тем является отсутствие классических коррелированных подзапросов в секции ВЫБРАТЬ. В стандартном SQL мы можем написать подзапрос прямо в списке выбираемых полей, передав в него параметр из основного запроса. В 1С такой подход не поддерживается напрямую, но мы проанализируем ситуацию и увидим, что те же задачи успешно решаются через соединения или подзапросы в секции ГДЕ.
Рассмотрим классическую задачу: получить список клиентов и дату их последнего заказа. Простейший способ сделать это в 1С — использовать подзапрос с агрегатной функцией МАКСИМУМ в условии связи или фильтрации. Рассмотрим пример кода:
ВЫБРАТЬ
Т1.Клиент КАК Клиент,
Т1.Дата КАК ДатаПоследнегоЗаказа,
Т1.Ссылка КАК Заказ
ИЗ
Документ.ЗаказКлиента КАК Т1
ГДЕ
Т1.Дата В (
ВЫБРАТЬ
МАКСИМУМ(Т2.Дата)
ИЗ
Документ.ЗаказКлиента КАК Т2
ГДЕ
Т2.Клиент = Т1.Клиент
)
Этот код демонстрирует, что использование корреляции в секции ГДЕ вполне допустимо. Однако для более сложных условий, например, когда нужно рассчитать количество дней, прошедших с момента предыдущего заказа, лучше использовать механизмы соединений. Разберем по шагам более сложный пример:
ВЫБРАТЬ
ВложенныйЗапрос.Контрагент КАК Контрагент,
Заказ.Ссылка КАК Ссылка,
РАЗНОСТЬДАТ(ВложенныйЗапрос.Дата, &ТекущаяДата, ДЕНЬ) КАК ДнейСПоследнегоЗаказа,
ВложенныйЗапрос.Дата КАК ДатаПоследнегоЗаказа
ИЗ
(ВЫБРАТЬ
Заказ.Контрагент КАК Контрагент,
МАКСИМУМ(Заказ.Дата) КАК Дата
ИЗ
Документ.Заказ КАК Заказ
СГРУППИРОВАТЬ ПО
Заказ.Контрагент) КАК ВложенныйЗапрос
ЛЕВОЕ СОЕДИНЕНИЕ
Документ.Заказ КАК Заказ
ПО
ВложенныйЗапрос.Контрагент = Заказ.Контрагент
И ВложенныйЗапрос.Дата = Заказ.Дата
В данном примере мы сначала получаем таблицу последних дат для каждого контрагента, а затем соединяем её с основной таблицей заказов. Это позволяет избежать избыточных вложенных циклов на уровне СУБД и работает значительно быстрее при наличии индексов по полям Контрагент и Дата.
Начиная с версии 8.3.24, в языке запросов 1С появилась поддержка оконных функций. Это нововведение делает обсуждение коррелированных подзапросов практически неактуальным для большинства задач. Теперь мы можем использовать конструкцию OVER, которая позволяет выполнять вычисления по группам строк (окнам), сохраняя при этом детальные записи. Рассмотрим, как это упрощает жизнь разработчику.
С помощью функций ROW_NUMBER() или RANK() задача получения последней записи решается в один проход без соединений:
ВЫБРАТЬ
Заказы.Ссылка,
Заказы.Контрагент,
НОМЕР_СТРОКИ() OVER (ПАРТИЦИОНИРОВАТЬ ПО Заказы.Контрагент УПОРЯДОЧИТЬ ПО Заказы.Дата УБЫВ) КАК НомерЗаказаПоПорядку
ИЗ
Документ.Заказ КАК Заказы
Отфильтровав результат, где НомерЗаказаПоПорядку = 1, мы получим срез последних данных. Оконные функции поддерживают не только нумерацию, но и агрегаты: SUM, MAX, MIN, AVG. Это позволяет, например, рассчитать нарастающий итог или долю строки в общем объеме данных прямо в запросе, что ранее требовало сложной обработки в коде или пакетных запросов.
Еще одной серьезной претензией к платформе долгое время была невозможность быстро обновлять данные запросами. Разработчикам приходилось использовать цикл, в котором выполнялись методы ПолучитьОбъект() и Записать(). Это создавало огромную нагрузку на сервер и СУБД. В версии 8.3.25 появился механизм Массового изменения данных.
Этот механизм позволяет выполнять операции, аналогичные UPDATE и DELETE в SQL, но с сохранением бизнес-логики платформы. Разберем основные преимущества этого подхода:
Для реализации массового изменения используется объект МассовоеИзменениеДанных. Мы можем указать источник данных (например, запрос) и установить соответствие полей для записи в целевую таблицу (регистр или табличную часть документа).
Многие разработчики, работавшие с FoxPro или Delphi, привыкли к точкам останова по изменению значения переменной. В классическом Конфигураторе 1С такой функции нет, что затрудняет поиск места, где ошибочно перезаписывается реквизит. Однако решение существует в рамках 1C:EDT (Enterprise Development Tools).
В 1C:EDT можно использовать так называемые Watchpoints (точки наблюдения). При их установке на переменную или поле объекта выполнение программы будет приостановлено именно в тот момент, когда значение этого поля изменится. Это избавляет от необходимости просматривать сотни строк кода в поисках виновника ошибки. Выясним причину бага за несколько секунд, просто наблюдая за состоянием памяти приложения.
Помимо языковых конструкций, важно учитывать архитектурные улучшения. Рассмотрим расширенные режимы замещения в регистрах (доступны с 8.3.21). Ранее при записи набора записей регистра накопления блокировалась вся таблица по конкретному регистратору. Теперь платформа позволяет перезаписывать только часть данных (по измерениям), что существенно повышает параллельность работы пользователей.
Также стоит отметить снятие ограничения на количество строк в табличных частях (ранее было 99 999 строк). В крупных системах это позволяет обрабатывать огромные спецификации или чеки в рамках одного документа, не прибегая к искусственному дроблению данных на несколько объектов.
Платформа 1С сегодня — это мощная среда разработки, которая во многих аспектах уже догнала классические СУБД. Проанализировав ситуацию, мы видим, что большинство ограничений, на которые жаловались разработчики прошлых лет, успешно сняты. Оконные функции, массовые обновления данных, современная среда разработки EDT и механизмы двустороннего взаимодействия сервера и клиента (WebSockets) позволяют создавать производительные и масштабируемые системы. Главное — следить за обновлениями платформы.