Условия, соединения и итоги: собираем запрос 1С от простого к сложному

· 10 мин

Содержание

Запрос в 1С собирается из секций в строгом порядке: ВЫБРАТЬ → ИЗ → СОЕДИНЕНИЕ … ПО → ГДЕ → СГРУППИРОВАТЬ ПО → ИМЕЮЩИЕ → УПОРЯДОЧИТЬ ПО → ИТОГИ. Условия отбора пишут в ГДЕ через параметры &Имя, связи таблиц — через ЛЕВОЕ СОЕДИНЕНИЕ, суммы — через СУММА() с группировкой, а тип поля приводят функцией ВЫРАЗИТЬ.

Типичная ситуация: главбух просит «выгрузку продаж по контрагентам за квартал, только где сумма больше 100 тысяч». Отчёта такого нет, а язык запросов 1С решает задачу за десяток строк. Ниже разобраны все конструкции, которые для этого нужны, с примерами на объектах «Бухгалтерии предприятия 3.0». Имена справочников и регистров в вашей конфигурации могут отличаться — сверяйтесь с деревом метаданных в Конфигураторе.

Как устроен запрос в 1С: каркас и синтаксис

Язык запросов 1С похож на SQL, но работает не с таблицами СУБД, а с объектами конфигурации: Справочник.Номенклатура, Документ.РеализацияТоваровУслуг, РегистрСведений.КурсыВалют. Платформа сама переводит текст в SQL нужной СУБД — файловой, MS SQL или PostgreSQL.

Главное отличие от SQL: язык запросов 1С только читает данные. Команд UPDATE, DELETE, INSERT в нём нет — запись идёт через объекты встроенного языка.

Минимальный запрос

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.Наименование КАК Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    НЕ Номенклатура.ПометкаУдаления
    И НЕ Номенклатура.ЭтоГруппа
УПОРЯДОЧИТЬ ПО
    Наименование

Что здесь есть:

  • 🔹 КАК задаёт псевдоним таблицы или поля. По псевдонимам потом обращаются к колонкам результата.
  • 🔹 Ссылка — уникальная ссылка на элемент. Её выбирают почти всегда, чтобы потом открыть объект.
  • 🔹 Ключевые слова пишутся в любом регистре. Работают и английские аналоги: SELECT, FROM, WHERE.

Модификаторы после ВЫБРАТЬ

МодификаторЧто делаетПример
РАЗЛИЧНЫЕУбирает дубли строкВЫБРАТЬ РАЗЛИЧНЫЕ Контрагент
ПЕРВЫЕ NВозвращает первые N строкВЫБРАТЬ ПЕРВЫЕ 10 …
РАЗРЕШЕННЫЕОтбрасывает записи, закрытые ограничением прав (RLS)ВЫБРАТЬ РАЗРЕШЕННЫЕ …

Как выполнить запрос из кода

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Номенклатура.Ссылка КАК Ссылка,
|    Номенклатура.Наименование КАК Наименование
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|    Номенклатура.Наименование ПОДОБНО &Шаблон";
Запрос.УстановитьПараметр("Шаблон", "%кабель%");

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование);
КонецЦикла;

Не набирайте текст вручную с нуля. В Конфигураторе в модуле вызовите контекстное меню → Конструктор запроса…: он сам расставит секции, псевдонимы и переносы |.

Условия в запросе 1С: ГДЕ, параметры и операторы

Условие отбора пишется в секции ГДЕ. Значения в текст не вклеивают, а передают параметром: &Дата, &Организация. Так запрос не ломается от кавычек в названиях и кэшируется СУБД.

Какими операторами выразить условие

  • ⚖️ Сравнение: =, <>, <, >, <=, >=.
  • 📅 Диапазон: Дата МЕЖДУ &Начало И &Конец — обе границы включаются.
  • 📋 Список: Склад В (&СписокСкладов) — параметром передают массив или список значений.
  • 🌳 Иерархия: Номенклатура В ИЕРАРХИИ (&Группа) — группа и всё вложенное.
  • 🔤 Шаблон строки: Наименование ПОДОБНО "%болт%". % — любое число символов, _ — один символ.
  • ∅ Пустота: Поле ЕСТЬ NULL и Поле ЕСТЬ НЕ NULL.
  • 📄 Тип ссылки: Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг.
  • 🔗 Логика: И, ИЛИ, НЕ и скобки.

Пример с периодом и предопределёнными значениями

ВЫБРАТЬ
    Реализация.Ссылка КАК Документ,
    Реализация.Контрагент КАК Контрагент,
    Реализация.СуммаДокумента КАК Сумма
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Проведен
    И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И Реализация.Контрагент <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)

Поле Дата у документа хранит и время. Если передать в &КонецПериода дату 31.03 без времени, документы за 31 марта после 00:00:00 в выборку не попадут. Передавайте КонецДня(ДатаОкончания).

Вычисляемое условие: ВЫБОР КОГДА

Когда значение поля зависит от условия, используют конструкцию-аналог CASE:

ВЫБОР
    КОГДА Реализация.СуммаДокумента >= 100000 ТОГДА "Крупная"
    КОГДА Реализация.СуммаДокумента >= 10000 ТОГДА "Средняя"
    ИНАЧЕ "Мелкая"
КОНЕЦ КАК КатегорияСделки

Как выразить соединение таблиц в запросе

Соединение нужно, когда данные лежат в разных таблицах: товары — в справочнике, остатки — в регистре, цены — в регистре сведений. Связь задаётся после слова ПО.

Вид соединенияЧто вернётКогда брать
ВНУТРЕННЕЕ СОЕДИНЕНИЕТолько строки, у которых есть пара в обеих таблицахНужны лишь совпадения
ЛЕВОЕ СОЕДИНЕНИЕВсе строки левой таблицы, справа — пара или NULL«Все товары и их цена, если она есть»
ПРАВОЕ СОЕДИНЕНИЕЗеркально левомуРедко; обычно таблицы меняют местами
ПОЛНОЕ СОЕДИНЕНИЕВсе строки обеих таблицСверка двух источников

Просто СОЕДИНЕНИЕ без уточнения означает внутреннее. Декартово произведение получают, перечислив таблицы в ИЗ через запятую.

Пример: все товары и их последняя цена

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
    Справочник.Номенклатура КАК Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
                &Дата, ТипЦен = &ТипЦен) КАК Цены
        ПО Номенклатура.Ссылка = Цены.Номенклатура
ГДЕ
    НЕ Номенклатура.ЭтоГруппа

Здесь два приёма, без которых левое соединение работает неправильно.

  1. ЕСТЬNULL. У товаров без цены поле Цены.Цена равно NULL. Функция ЕСТЬNULL(Поле, 0) подставит ноль, и суммирование не сломается.
  2. Где ставить отбор по правой таблице. Условие ТипЦен = &ТипЦен стоит в параметрах виртуальной таблицы, а не в ГДЕ.

Отбор по полю правой таблицы в секции ГДЕ превращает левое соединение во внутреннее: строки с NULL справа не проходят условие и пропадают. Такое условие пишут в ПО или в параметрах виртуальной таблицы.

Неявное соединение через точку

Запись Реализация.Контрагент.ИНН — это тоже соединение: платформа сама подтянет таблицу контрагентов. Удобно, но у составных полей (например, Регистратор в регистре) такая точка соединяет сразу со всеми возможными типами документов. Об этом — в разделе об оптимизации.

Группировка и агрегатные функции в языке запросов

Агрегатные функции сворачивают много строк в одну:

  • ➕ СУММА(Поле) — сумма;
  • 🔢 КОЛИЧЕСТВО(Поле) и КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле) — число строк или уникальных значений;
  • 📈 МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ.

Правило одно: каждое поле в ВЫБРАТЬ без агрегатной функции обязано стоять в СГРУППИРОВАТЬ ПО. Иначе запрос не выполнится с ошибкой Поле не входит в группу.

Задача главбуха: продажи по контрагентам больше 100 тысяч

ВЫБРАТЬ
    Товары.Ссылка.Контрагент КАК Контрагент,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Товары.Ссылка) КАК ЧислоДокументов,
    СУММА(Товары.Сумма) КАК Выручка
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
    Товары.Ссылка.Проведен
    И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    Товары.Ссылка.Контрагент
ИМЕЮЩИЕ
    СУММА(Товары.Сумма) > 100000
УПОРЯДОЧИТЬ ПО
    Выручка УБЫВ

Разница между ГДЕ и ИМЕЮЩИЕ:

СекцияКогда срабатываетЧто можно проверять
ГДЕДо группировки, по каждой строкеОбычные поля
ИМЕЮЩИЕПосле группировкиРезультаты агрегатных функций

Группировка по месяцам

Чтобы свернуть продажи по месяцам, дату приводят к началу периода:

НАЧАЛОПЕРИОДА(Товары.Ссылка.Дата, МЕСЯЦ) КАК Месяц

Это же выражение повторяют в СГРУППИРОВАТЬ ПО. Аналогично работают ГОД(), КВАРТАЛ(), ДЕНЬНЕДЕЛИ(), а для разницы дат — РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ).

Итоги для отчёта

Секция ИТОГИ СУММА(Выручка) ПО ОБЩИЕ, Контрагент добавляет к результату строки промежуточных и общих итогов. Обходят такой результат выборкой с параметром ОбходРезультатаЗапроса.ПоГруппировкам.

Вложенные запросы, временные таблицы и пакеты

Вложенный запрос

Подзапрос в условии — самый короткий способ отобрать данные по другой таблице:

ГДЕ
    Контрагенты.Ссылка В (
        ВЫБРАТЬ РАЗЛИЧНЫЕ Реализация.Контрагент
        ИЗ Документ.РеализацияТоваровУслуг КАК Реализация
        ГДЕ Реализация.Дата >= &НачалоПериода)

Вложенный запрос в ИЗ или в соединении тоже допустим, но СУБД плохо оценивает его размер. На больших базах это частая причина медленных отчётов.

Временные таблицы и пакетный запрос

Надёжнее разбить задачу на шаги: результат первого шага положить во временную таблицу, а потом соединяться с ней. Запросы пакета разделяют точкой с запятой.

ВЫБРАТЬ
    Реализация.Контрагент КАК Контрагент,
    СУММА(Реализация.СуммаДокумента) КАК Выручка
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Проведен
    И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    Реализация.Контрагент
ИНДЕКСИРОВАТЬ ПО
    Контрагент
;

////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Контрагенты.Ссылка КАК Контрагент,
    Контрагенты.ИНН КАК ИНН,
    ЕСТЬNULL(ВТ_Продажи.Выручка, 0) КАК Выручка
ИЗ
    Справочник.Контрагенты КАК Контрагенты
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК ВТ_Продажи
        ПО Контрагенты.Ссылка = ВТ_Продажи.Контрагент
ГДЕ
    НЕ Контрагенты.ЭтоГруппа

Как это работает:

  1. ПОМЕСТИТЬ ставят сразу после списка полей, до ИЗ.
  2. ИНДЕКСИРОВАТЬ ПО строит индекс по полю, по которому потом будет соединение. На малых таблицах он не нужен, на десятках тысяч строк — ускоряет.
  3. Запрос.Выполнить() вернёт результат последнего запроса пакета, Запрос.ВыполнитьПакет() — массив результатов всех запросов.
  4. Чтобы временная таблица жила между разными запросами, создайте Новый МенеджерВременныхТаблиц и присвойте его свойству Запрос.МенеджерВременныхТаблиц.

Ненужную временную таблицу удаляют командой УНИЧТОЖИТЬ ВТ_Продажи отдельным запросом пакета. Это освобождает память, когда пакет длинный.

Объединение результатов

ОБЪЕДИНИТЬ ВСЕ склеивает результаты двух запросов в одну таблицу. Число и порядок полей должны совпадать, псевдонимы берутся из первого запроса.

Функция ВЫРАЗИТЬ и быстрые запросы: индексы, план, ошибки

Как выразить тип поля

Функция ВЫРАЗИТЬ приводит значение к нужному типу. Без неё не обойтись в трёх случаях.

ЗадачаЗапись
Сравнить или сгруппировать строку неограниченной длины (например, Комментарий)ВЫРАЗИТЬ(Док.Комментарий КАК СТРОКА(200))
Взять реквизит у составного поля, зная типВЫРАЗИТЬ(Остатки.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент
Задать разрядность числаВЫРАЗИТЬ(Сумма / Количество КАК ЧИСЛО(15, 2))

Второй случай — главный приём ускорения. Запись Регистратор.Контрагент заставляет платформу соединиться со всеми таблицами документов, которые пишут в регистр. ВЫРАЗИТЬ оставляет одно соединение.

Что тормозит запросы

  • 🐢 Запрос в цикле. Сотня запросов по одной строке медленнее одного запроса на сто строк. Собирайте ссылки в массив и передавайте через В (&Массив).
  • 🐢 Отбор виртуальной таблицы в ГДЕ. Условие по измерению пишите в параметрах: Остатки(&Период, Склад = &Склад). Тогда платформа отберёт данные до расчёта остатков, а не после.
  • 🐢 ИЛИ по разным полям. Условие А = &X ИЛИ Б = &Y мешает СУБД использовать индекс. Часто быстрее два запроса через ОБЪЕДИНИТЬ ВСЕ.
  • 🐢 Соединение с подзапросом. Замените временной таблицей с индексом.
  • 🐢 Лишняя точка. Каждая Ссылка.Реквизит.Реквизит — новое соединение. Выбирайте только то, что выводите.
  • 🐢 РАЗЛИЧНЫЕ «на всякий случай». Это сортировка всего результата. Ставьте только там, где дубли реально возможны.

Как посмотреть, что делает СУБД

Индексы в 1С создаются не командой SQL, а свойствами метаданных. Измерения регистров индексируются автоматически, у реквизитов есть свойство Индексировать. Добавлять индекс без замера смысла нет.

Порядок замера такой:

  1. В Конфигураторе включите Отладка → Замер производительности и выполните код с запросом — увидите время по строкам.
  2. Для плана выполнения настройте технологический журнал: в файле logcfg.xml событие DBMSSQL (или DBPOSTGRS для PostgreSQL) и элемент <plansql/>.
  3. На MS SQL план также показывает SQL Server Profiler, на PostgreSQL — EXPLAIN ANALYZE по тексту запроса из журнала.

Технологический журнал на рабочем сервере быстро растёт. Включайте его коротким окном и с фильтром по базе, потом выключайте.

Проверьте себя
Секция, в которой проверяют результат СУММА() после группировки?
Отбор по правой таблице левого соединения поставили в ГДЕ. Что произойдёт?
Как сгруппировать по полю «Комментарий» неограниченной длины?

Примеры запросов 1С под частые задачи

Остатки по счёту на дату

Для «Бухгалтерии предприятия» остатки берут из регистра бухгалтерии Хозрасчетный:

ВЫБРАТЬ
    Остатки.Субконто1 КАК Номенклатура,
    Остатки.КоличествоОстаток КАК Количество,
    Остатки.СуммаОстаток КАК Сумма
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Остатки(
        &Дата,
        Счет В ИЕРАРХИИ (&Счет41),
        ,
        Организация = &Организация) КАК Остатки

Параметр &Счет41 — ссылка на счёт 41 из плана счетов Хозрасчетный: с В ИЕРАРХИИ попадут и все его субсчета. Учтите: остатки на дату берутся на начало этого момента, поэтому для конца дня передают Новый Граница(КонецДня(Дата), ВидГраницы.Включая).

Дубли в справочнике

ВЫБРАТЬ
    Контрагенты.ИНН КАК ИНН,
    КОЛИЧЕСТВО(Контрагенты.Ссылка) КАК Сколько
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    Контрагенты.ИНН <> ""
    И НЕ Контрагенты.ПометкаУдаления
СГРУППИРОВАТЬ ПО
    Контрагенты.ИНН
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(Контрагенты.Ссылка) > 1

Документы без проводок

Проведённый документ, по которому нет записей в регистре, ищут левым соединением с проверкой на NULL:

ВЫБРАТЬ
    Реализация.Ссылка КАК Документ
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный КАК Проводки
        ПО Реализация.Ссылка = Проводки.Регистратор
ГДЕ
    Реализация.Проведен
    И Проводки.Регистратор ЕСТЬ NULL

Здесь проверка правой таблицы в ГДЕ стоит намеренно: нам нужны как раз строки без пары.

Отладка запроса без программирования

Готовый текст удобно проверять во внешней обработке «Консоль запросов»: вставляете запрос, задаёте параметры, смотрите результат таблицей. Запускается она в режиме 1С:Предприятие через Файл → Открыть. Права на открытие внешних обработок у пользователя должны быть.

Частые вопросы

Чем ОБЪЕДИНИТЬ отличается от ОБЪЕДИНИТЬ ВСЕ?
ОБЪЕДИНИТЬ удаляет одинаковые строки, ОБЪЕДИНИТЬ ВСЕ оставляет все. Второй вариант быстрее — используйте его, если дублей быть не может.
Можно ли изменить данные запросом 1С?
Нет. Язык запросов только читает данные. Менять записи нужно через объекты встроенного языка или наборы записей регистров.
Как убрать время из даты в результате запроса?
Используйте НАЧАЛОПЕРИОДА(Поле, ДЕНЬ). Значение останется датой, но со временем 00:00:00, и по нему можно группировать.
Почему СУММА вернула пусто, а не ноль?
Если в группе все значения NULL, агрегатная функция тоже даёт NULL. Оберните её: ЕСТЬNULL(СУММА(Поле), 0).
Работает ли в запросе 1С синтаксис SQL, например TOP или LIMIT?
Английские ключевые слова 1С поддерживает (SELECT, WHERE), но это синтаксис самого языка запросов 1С. Вместо TOP/LIMIT пишут ПЕРВЫЕ N (TOP N) сразу после ВЫБРАТЬ.