Работа с прямыми запросами в 1С 7.7 через компоненту 1С++ — это мощный инструмент, который позволяет существенно ускорить получение данных из базы SQL. Однако при переходе со штатных запросов на SQL-синтаксис программисты часто сталкиваются с тем, что часть данных "исчезает" из выборки или привычные методы группировки перестают работать. В этой статье мы подробно разберем, как правильно соединять таблицы, чтобы не терять документы с пустыми реквизитами, и как эффективно организовать группировку итогов.
Рассмотрим типичную ситуацию: мы пишем запрос к документам и хотим подтянуть данные из справочника (например, сведения о клиенте или торговом представителе). Если использовать стандартное соединение 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.
Чтобы 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С++), который обладает мощными методами постобработки. Разберем последовательность действий:
ИТЗ.ИТЗ.Группировать() или строим индексы для быстрого поиска.ВыбратьСтроки() с последующим обходом, имитируя работу штатного запроса.Этот подход часто оказывается быстрее, чем попытки заставить 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 и объектов ИТЗ дает полный контроль над итогами и группировками данных.