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

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

При работе с платформой 1С:Предприятие 7.7 программисты часто прибегают к использованию прямых запросов (через компоненты 1C++ или Rainbow) для повышения производительности. Одной из типовых задач является получение данных из шапки документа (журнала) одновременно с информацией о количестве строк в его табличной части. Стандартными средствами метаданных это делается легко, но в SQL нам нужно вручную связывать таблицы журнала и таблицы строк документов.

В этой статье мы разберем несколько эффективных способов решения этой задачи, проанализируем их производительность и выясним, какой метод лучше использовать в зависимости от версии вашего SQL-сервера.

Структура данных 1С 7.7 в SQL

Прежде чем переходить к коду, вспомним, как хранятся данные. Информация о документах распределена следующим образом:

Способ №1: Использование конструкции OUTER APPLY

Этот метод стал доступен начиная с SQL Server 2005 и является одним из самых элегантных и современных способов решения задачи. Разберем пример, предложенный в сообщении форума.

Конструкция OUTER APPLY работает как коррелированный подзапрос, но позволяет SQL-серверу более гибко строить план выполнения. Посмотрим на код:


SELECT
    жур.IDDOC as [Документ_ID],
    жур.DOCNO as [НомерДокумента],
    СтрокиДокументов.Количество as [КоличествоСтрок]
FROM
    _1SJOURN as жур 
OUTER APPLY (
    SELECT 
        COUNT(*) as [Количество]
    FROM 
        $ДокументСтроки.Реализация as док
    WHERE 
        док.IDDOC = жур.IDDOC
) as СтрокиДокументов
WHERE 
    жур.IDDOCDEF = $ВидДокумента.Реализация

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

  1. Лаконичность: Нам не нужно группировать основную выборку.
  2. Производительность: SQL Server может распараллеливать выполнение APPLY на несколько ядер (особенно в версиях 2014+), что значительно ускоряет обработку больших массивов данных.
  3. Удобство: Нам не нужно повторять все условия фильтрации в соединении, так как подзапрос внутри APPLY "видит" текущую строку из таблицы _1SJOURN.

Способ №2: Классическое левое соединение (LEFT JOIN) с группировкой

Если вы используете старые версии SQL или предпочитаете классический синтаксис, можно использовать соединение с предварительно сгруппированной таблицей строк. Этот метод считается «золотым стандартом» совместимости.

Разберем, как это реализовать:


SELECT 
    jur.IDDOC as [Док],
    ISNULL(T_Count.Cnt, 0) as [КоличествоСтрок]
FROM 
    _1SJOURN as jur
LEFT JOIN (
    SELECT 
        IDDOC, 
        COUNT(*) as Cnt
    FROM 
        $ДокументСтроки.Реализация
    GROUP BY 
        IDDOC
) as T_Count ON T_Count.IDDOC = jur.IDDOC
WHERE 
    jur.IDDOCDEF = $ВидДокумента.Реализация

Обратите внимание на использование функции ISNULL. При использовании LEFT JOIN, если у документа по какой-то причине нет строк, запрос вернет NULL. Оборачивание в ISNULL(..., 0) гарантирует получение корректного числового значения.

Когда выбирать этот метод? На больших выборках (например, при получении итогов за несколько лет) SQL Server часто строит план типа Hash Match, который может отработать быстрее, чем последовательный перебор строк в APPLY.

Способ №3: Простой вложенный подзапрос в блоке SELECT

Если вам нужно получить количество строк только для одного небольшого отчета и вы не хотите усложнять структуру JOIN, можно вставить подзапрос прямо в список выбираемых полей.


SELECT 
    jur.IDDOC,
    (SELECT COUNT(*) FROM $ДокументСтроки.Реализация as dt WHERE dt.IDDOC = jur.IDDOC) as [К-во строк]
FROM 
    _1SJOURN as jur
WHERE 
    jur.IDDOCDEF = $ВидДокумента.Реализация

Этот способ максимально нагляден, но будьте осторожны: на очень больших объемах данных (сотни тысяч строк) он может проигрывать в скорости первым двум вариантам.

Важные технические нюансы

При реализации подсчета строк в 1С 7.7 следует учитывать специфику платформы:

1. Поле LINENO_ или COUNT(*)?
В таблицах DT* всегда есть поле LINENO_ (номер строки). Иногда возникает соблазн использовать MAX(LINENO_) вместо COUNT(*). Мы не рекомендуем так делать. Если в базе проводились прямые манипуляции с данными или были сбои, нумерация строк может иметь пропуски. COUNT(*) всегда вернет фактическое количество записей.

2. Подсчет для разных видов документов
Если ваш запрос выбирает данные по нескольким видам документов сразу, задача усложняется, так как строки каждого вида лежат в своей таблице. В этом случае придется использовать либо несколько LEFT JOIN с CASE, либо предварительно собирать данные через UNION ALL во временную таблицу:


-- Пример с UNION для разных ТЧ
SELECT 
    IDDOC, COUNT(*) as Cnt 
INTO #TempLines
FROM (
    SELECT IDDOC FROM $ДокументСтроки.Реализация
    UNION ALL
    SELECT IDDOC FROM $ДокументСтроки.Поступление
) as T
GROUP BY IDDOC

3. Влияние индексов
Для быстрой работы любого из приведенных выше запросов SQL-сервер использует кластерный индекс таблицы табличной части. В 1С 7.7 этот индекс всегда строится по полю IDDOC. Поэтому, пока вы фильтруете или связываете данные только по IDDOC, запросы будут летать.

Резюме

Мы рассмотрели три основных способа получения количества строк. Для современных систем на базе SQL Server 2008 и выше наиболее рекомендуемым является использование OUTER APPLY благодаря его читаемости и потенциалу оптимизации. Для универсальных решений, работающих на старых версиях, используйте LEFT JOIN с предварительной группировкой. Не забывайте про обработку NULL значений и всегда проверяйте планы выполнения на реальных объемах данных.

← На главную