Как правильно объединять таблицы и строить группировки в прямых SQL-запросах на 1С 7.7

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Работа с прямыми запросами в 1С 7.7 через компоненту 1С++ — это мощный инструмент, который позволяет существенно ускорить получение данных из базы SQL. Однако при переходе со штатных запросов на SQL-синтаксис программисты часто сталкиваются с тем, что часть данных "исчезает" из выборки или привычные методы группировки перестают работать. В этой статье мы подробно разберем, как правильно соединять таблицы, чтобы не терять документы с пустыми реквизитами, и как эффективно организовать группировку итогов.

Проблема исчезновения строк при использовании INNER JOIN

Рассмотрим типичную ситуацию: мы пишем запрос к документам и хотим подтянуть данные из справочника (например, сведения о клиенте или торговом представителе). Если использовать стандартное соединение INNER JOIN, то в результат попадут только те строки, для которых найдено соответствие в обеих таблицах. Если в документе реквизит КлиентСпр не заполнен, SQL-сервер просто исключит эту строку из выдачи.

Для решения этой задачи мы должны использовать LEFT JOIN. Разберем механику: LEFT JOIN берет все записи из левой таблицы (основной документ) и пытается присоединить к ним данные из правой таблицы (справочник). Если соответствие не найдено, поля правой таблицы заполняются значением NULL, но сама строка документа остается в выборке.

Посмотрим на пример исправленного запроса:


ТекстЗапроса="
|SELECT                     
|  Док.IDDoc as [Док $Документ.Накладная],
|  Жур.DOCNO as НомерДок,
|  $Док.Клиент as Клиент, 
|  $СпрКл.ТорговыйПредставитель as [ТорговыйПредставитель $Справочник.Клиенты]
|FROM
|  $Документ.Накладная as Док
|INNER JOIN
|  _1SJourn as Жур ON Жур.IDDoc = Док.IDDoc 
|LEFT JOIN
|  $Справочник.Клиенты as СпрКл ON СпрКл.Id = $Док.КлиентСпр
|";

Важно помнить, что при использовании LEFT JOIN в полях справочника может появиться NULL. В 1С 7.7 это может привести к ошибкам при последующей обработке таблицы значений. Чтобы этого избежать, проанализируем ситуацию и применим функцию ISNULL.

Обработка пустых значений (NULL) в результатах запроса

Чтобы 1С корректно воспринимала пустые ссылки, мы рекомендуем заменять NULL на пустую строку идентификатора. Для справочников в 1С 7.7 это обычно строка из 13 символов (пробелы и нули). Рассмотрим, как модифицировать поле выбора:

ISNULL($СпрКл.ТорговыйПредставитель, ' 0 ') as [ТорговыйПредставитель $Справочник.Клиенты]

Таким образом, если клиент не указан или у него нет представителя, 1С получит "ПустоеЗначение", а не ошибку типа.

Реализация группировок в прямых запросах

В штатных запросах 1С мы привыкли использовать конструкцию Группировка("Товар"). В прямых SQL-запросах такого метода нет, так как результат возвращается в виде плоской таблицы. Выясним, какие существуют пути решения этой задачи.

Способ 1: Использование WITH ROLLUP

Если ваша база работает на MS SQL Server, можно использовать оператор WITH ROLLUP в секции GROUP BY. Это заставит SQL-сервер добавить в выборку дополнительные строки с промежуточными и общими итогами. Проанализируем пример:


|GROUP BY 
|  Рег.Товар, Рег.Склад WITH ROLLUP

В этом случае в таблице появятся строки, где поле Товар заполнено, а Склад содержит NULL — это будет итог по товару. Строка, где оба поля NULL, будет являться общим итогом.

Способ 2: Использование Индексированных Таблиц (ИТЗ)

Это наиболее гибкий и "1С-ный" метод. Мы выгружаем результат запроса в объект ИндексированнаяТаблица (компонента 1С++), который обладает мощными методами постобработки. Разберем последовательность действий:

  1. Выполняем SQL-запрос и получаем результат в объект ИТЗ.
  2. Используем метод ИТЗ.Группировать() или строим индексы для быстрого поиска.
  3. Если нужна иерархия, используем метод ВыбратьСтроки() с последующим обходом, имитируя работу штатного запроса.

Этот подход часто оказывается быстрее, чем попытки заставить SQL-сервер сразу выдать иерархическую структуру.

Оптимизация: Виртуальные таблицы и блокировки

При работе с регистрами (например, через макрос $РегистрОстаткиОбороты) важно понимать структуру хранения данных. 1С 7.7 хранит итоги в таблицах RG*, а движения в RA*. Прямой запрос к виртуальной таблице объединяет их автоматически, но мы должны заботиться о производительности системы в целом.

Рассмотрим использование хинта (nolock). В высоконагруженных базах чтение данных может блокировать запись (проведение документов). Чтобы отчет не останавливал работу всей фирмы, всегда добавляйте (nolock) к физическим таблицам:


|FROM
|  $Регистр.ОстаткиТоваров as Рег (nolock)

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

Работа с идентификаторами объектов

При написании условий соединения JOIN или фильтров WHERE вручную (без использования типизированных макросов), помните о формате ID. В 1С 7.7 идентификаторы в SQL хранятся в 36-ричной системе счисления. Если вы сравниваете ID из журнала документов _1SJourn с IDDoc в таблице самого документа, проблем обычно нет. Однако при ручном поиске элементов справочников важно учитывать длину строки и возможные префиксы.

Для передачи списков (например, фильтр по списку товаров) используйте конструкцию IN (:ВыбСписок). Компонента 1С++ автоматически преобразует СписокЗначений 1С в строку идентификаторов, понятную SQL-серверу, что избавляет нас от необходимости писать сложные циклы сборки текста запроса.

Резюмируем: использование LEFT JOIN вместо INNER JOIN позволяет сохранить целостность выборки при наличии пустых связей, а комбинация WITH ROLLUP и объектов ИТЗ дает полный контроль над итогами и группировками данных.

← На главную