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

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

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

Анализ причины ошибки: почему INNER JOIN возвращает пустоту

Разберем типичную ситуацию: автору необходимо подсчитать количество продаж определенного товара по трем разным видам документов. В исходном запросе использовалось последовательное соединение таблиц через INNER JOIN по полю IDDoc. С точки зрения архитектуры базы данных 1С 7.7, это критическая ошибка.

Выясним причину: в 1С 7.7 данные каждого вида документа хранятся в своих физических таблицах (шапки в DH*, строки в DT*). Поле IDDoc является уникальным идентификатором документа во всей системе. Один и тот же документ не может одновременно являться и "Реализацией", и "Розницей". Когда мы пишем INNER JOIN между тремя таблицами разных документов по IDDoc, мы буквально просим SQL-сервер: "Найди мне такую строку, идентификатор которой одновременно существует во всех трех таблицах". Естественно, таких строк не существует, и результат всегда будет пустым.

Даже добавление условий в блок WHERE по полю IDDOCDEF (идентификатор вида документа) не исправит ситуацию, так как сама логика соединения таблиц остается ошибочной. Рассмотрим правильные способы решения этой задачи.

Решение №1: Использование UNION ALL для объединения выборок

Самый логичный способ собрать данные из разных таблиц — это выполнить выборку из каждой таблицы по отдельности и объединить результаты с помощью оператора UNION ALL. Рассмотрим, как это реализовать структурно.

Проанализируем пример кода, который позволяет получить общие данные по всем документам:


SELECT
    SubQuery.Номенклатура [Номенклатура],
    SUM(SubQuery.КолПродаж) AS ОбщееКолПродаж
FROM (
    SELECT
        $ДокР.Номенклатура [Номенклатура],
        COUNT(DISTINCT ДокР.IDDoc) AS КолПродаж
    FROM
        _1SJourn Жур
    INNER JOIN
        $ДокументСтроки.Реализация AS ДокР ON ДокР.IDDoc = Жур.IDDoc
    WHERE
        Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~
        AND (Жур.CLOSED & 1 = 1)
        AND ($ДокР.Номенклатура = :ВбрТовар)
    GROUP BY $ДокР.Номенклатура

    UNION ALL

    SELECT
        $ДокРоз.Номенклатура [Номенклатура],
        COUNT(DISTINCT ДокРоз.IDDoc) AS КолПродаж
    FROM
        _1SJourn Жур
    INNER JOIN
        $ДокументСтроки.Реализация_Розница AS ДокРоз ON ДокРоз.IDDoc = Жур.IDDoc
    WHERE
        Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~
        AND (Жур.CLOSED & 1 = 1)
        AND ($ДокРоз.Номенклатура = :ВбрТовар)
    GROUP BY $ДокРоз.Номенклатура

    UNION ALL

    SELECT
        $ДокРасп.Номенклатура [Номенклатура],
        COUNT(DISTINCT ДокРасп.IDDoc) AS КолПродаж
    FROM
        _1SJourn Жур
    INNER JOIN
        $ДокументСтроки.Реализация_Распоряжение AS ДокРасп ON ДокРасп.IDDoc = Жур.IDDoc
    WHERE
        Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~
        AND (Жур.CLOSED & 1 = 1)
        AND ($ДокРасп.Номенклатура = :ВбрТовар)
    GROUP BY $ДокРасп.Номенклатура
) AS SubQuery
GROUP BY SubQuery.Номенклатура

Важные моменты в этом решении:

  1. Вложенный запрос (Subquery): Мы объединяем результаты трех выборок во временный набор данных.
  2. Внешняя группировка: После объединения через UNION ALL нам необходимо еще раз сгруппировать данные по номенклатуре и просуммировать значения КолПродаж, чтобы получить итоговую цифру.
  3. Фильтрация по дате: Обратите внимание на конструкцию :КонДата~. В 1С++ символ тильды после параметра даты автоматически добавляет время 23:59:59, что позволяет корректно захватить документы за последнюю секунду дня.
  4. Проверка проведенности: Условие (Жур.CLOSED & 1 = 1) гарантирует, что мы учитываем только проведенные документы.

Решение №2: Запрос к таблицам регистров (Оптимальный способ)

Если задача стоит в получении итоговых данных по продажам, то обращение напрямую к таблицам строк документов (DT*) является не самым эффективным методом. В 1С 7.7 для этих целей существуют регистры. Рассмотрим, почему это лучше.

В регистрах накопления (например, "ПартииТМЦ" или "Продажи") данные уже консолидированы. Вместо того чтобы сканировать три разные таблицы документов, мы можем обратиться к одной таблице движений регистра RA*. Это значительно ускоряет выполнение запроса, так как таблицы регистров оптимизированы для аналитики.

Разберем пример запроса к регистру:


SELECT 
    COUNT(DISTINCT Рег.IDDoc) AS КолПродаж,
    $Рег.Товар AS [Товар $Справочник.Номенклатура]
FROM 
    $Регистр.ПартииТМЦ AS Рег
INNER JOIN 
    _1SJourn AS Жур ON Рег.IDDoc = Жур.IDDoc
WHERE 
    Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~
    AND $Рег.Товар = :ВбрТовар
    AND Жур.IDDocDef IN ($ВидДокумента.Реализация, $ВидДокумента.Реализация_Розница, $ВидДокумента.Реализация_Распоряжение)
GROUP BY 
    $Рег.Товар

Преимущества данного подхода:

Рекомендации по работе с метапарсером 1С++

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

1. Обращение к полям: Вместо прямых имен таблиц и полей используйте конструкции типа $ДокументСтроки.ИмяДокумента и $ДокументСтроки.ИмяДокумента.Номенклатура. Это позволит 1С++ самостоятельно подставить актуальные имена таблиц (DT*) и колонок (SP*).

2. Работа с типами: При использовании LEFT JOIN помните, что если данных в присоединенной таблице нет, SQL вернет NULL. В 1С 7.7 это может привести к ошибкам в агрегатных функциях. Используйте COALESCE(Поле, 0) или ISNULL(Поле, 0) для обработки пустых значений.

3. Индексация: Поле Date_Time_IDDoc в таблице _1SJourn является частью кластерного индекса. Всегда ставьте условие по этому полю в начало блока WHERE, чтобы запрос работал максимально быстро.

Подводя итог, можно сказать, что решение проблемы "пустого результата" заключается в понимании структуры хранения данных 1С 7.7. Используйте UNION ALL, если вам действительно нужны данные из ТЧ документов, или переходите на запросы к регистрам для повышения производительности и упрощения кода.

← На главную