Запрос 1С 8.3 — основной способ прочитать данные из базы: остатки на складе, продажи за месяц, документы по условию. Язык запросов 1С похож на SQL, но работает с объектами конфигурации — справочниками, документами, регистрами — и умеет то, чего в SQL нет: получать реквизиты ссылок через точку, считать остатки виртуальными таблицами, добавлять итоги по группировкам. Ниже — структура запроса, порядок обработки секций, шпаргалка «что писать в какой секции» и три примера на типовых УТ 11 и БП 3.0.
Текст запроса — обычная строка, а выполняет его объект Запрос. Он работает на сервере, в толстом клиенте и во внешнем соединении, но недоступен в тонком клиенте и веб-клиенте. Поэтому в управляемой форме код с запросом пишут в процедуре с директивой &НаСервере или &НаСервереБезКонтекста, либо выносят в серверный общий модуль, модуль менеджера или объекта. Ещё одно место — схема компоновки данных: отчёт на СКД строится на тексте запроса.
Руками весь текст писать не обязательно: пункт контекстного меню модуля «Конструктор запроса» соберёт текст по выбранным таблицам и полям, а «Конструктор запроса с обработкой результата» сразу добавит цикл обхода. Отлаживать запрос удобнее в консоли, не меняя конфигурацию, — подробно в статье про консоль запросов 1С.
Минимальный рабочий каркас выглядит так:
&НаСервере
Процедура ВывестиКонтрагентовНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Наименование КАК Наименование,
| Контрагенты.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| НЕ Контрагенты.ПометкаУдаления
|
|УПОРЯДОЧИТЬ ПО
| Наименование";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование + ", ИНН " + Выборка.ИНН);
КонецЦикла;
КонецПроцедуры
Текст запроса открывается кавычкой, а каждая следующая строка начинается с вертикальной черты |: так во встроенном языке записывается любой многострочный строковый литерал, не только запрос. Метод Выполнить() возвращает РезультатЗапроса с тремя главными методами: Выбрать() — последовательный обход, Выгрузить() — таблица значений, Пустой() — проверка, что строк нет.
Шаблон одного запроса выборки: в квадратных скобках необязательные части, обязательны только ВЫБРАТЬ и список полей.
ВЫБРАТЬ [РАЗРЕШЕННЫЕ] [РАЗЛИЧНЫЕ] [ПЕРВЫЕ N]
<поля и выражения> КАК <псевдоним>, ...
[ПОМЕСТИТЬ <ИмяВременнойТаблицы>]
[ИЗ
<таблицы, виртуальные таблицы, соединения>]
[ГДЕ
<условия на исходные строки>]
[СГРУППИРОВАТЬ ПО
<поля группировки>]
[ИМЕЮЩИЕ
<условия на агрегаты>]
[ОБЪЕДИНИТЬ [ВСЕ]
ВЫБРАТЬ ...]
[УПОРЯДОЧИТЬ ПО
<поля> [ВОЗР | УБЫВ]]
[АВТОУПОРЯДОЧИВАНИЕ]
[ИТОГИ <агрегатные функции>
ПО [ОБЩИЕ,] <поля группировок>]
[ДЛЯ ИЗМЕНЕНИЯ [<таблицы>]]
[ИНДЕКСИРОВАТЬ ПО
<поля временной таблицы>]
Редкие предложения: АВТОУПОРЯДОЧИВАНИЕ поручает сортировку платформе, ДЛЯ ИЗМЕНЕНИЯ действует только в автоматическом режиме блокировок (в управляемом — объект БлокировкаДанных). ИНДЕКСИРОВАТЬ ПО пишут только в запросе с ПОМЕСТИТЬ — это индекс временной таблицы по полям будущего соединения. ИТОГИ с ПОМЕСТИТЬ не сочетаются.
Несколько запросов можно выполнить одним вызовом: их разделяют точкой с запятой, и получается пакетный запрос. Запрос с секцией ПОМЕСТИТЬ сохраняет результат во временную таблицу, которую следующие запросы пакета используют как обычный источник в ИЗ.
Текст читается сверху вниз, но логически секции обрабатываются в другом порядке:
РАЗЛИЧНЫЕ убирает повторы.ПЕРВЫЕ N. Исключение — запрос с ОБЪЕДИНИТЬ: там ПЕРВЫЕ ограничивает только свою часть ещё до общей сортировки, то есть берёт произвольные N строк.Физический порядок операций выбирает оптимизатор, но результат всегда такой, как если бы секции отработали в этом порядке. Отсюда правила:
ВЫБРАТЬ нельзя использовать в ГДЕ и ИМЕЮЩИЕ — на этом шаге его ещё нет; в УПОРЯДОЧИТЬ ПО и ИТОГИ — можно;ГДЕ — ошибка: условие на сумму по группе пишут в ИМЕЮЩИЕ;ПЕРВЫЕ 10 без УПОРЯДОЧИТЬ ПО возвращает какие попало десять строк, а не «топ-10»;ГДЕ (=, <>, В, >) с полем правой таблицы ЛЕВОГО СОЕДИНЕНИЯ отсекает строки с NULL в этом поле, и соединение фактически становится внутренним — такое условие переносят в ПО. Исключение — Правая.Поле ЕСТЬ NULL: оно оставляет только строки без пары.| Секция | Что сюда писать | Чего здесь быть не должно |
|---|---|---|
ВЫБРАТЬ | Поля, выражения, КАК с псевдонимом, агрегатные функции (СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ), ВЫБОР КОГДА … ТОГДА … КОНЕЦ, ЕСТЬNULL. См. строковые функции и функцию ЗНАЧЕНИЕ() | Полей без агрегатной функции, которых нет в группировке |
ПОМЕСТИТЬ | Имя временной таблицы для следующих запросов пакета. См. таблицу значений в запросе | Лишних временных таблиц, если результат нужен только коду |
ИЗ | Таблицы метаданных, табличные части, временные и виртуальные таблицы (Остатки, Обороты, СрезПоследних) с условиями в параметрах, соединения. См. поля ссылок через точку | Соединений «на всякий случай»: каждое замедляет запрос |
ГДЕ | Условия на исходные строки: =, <>, В, МЕЖДУ, ПОДОБНО, ССЫЛКА, ЕСТЬ NULL, параметры &Имя. См. даты в запросе и запросы к документам | Агрегатных функций и псевдонимов |
СГРУППИРОВАТЬ ПО | Все поля из ВЫБРАТЬ, которые не обёрнуты в агрегатную функцию | Агрегатных функций |
ИМЕЮЩИЕ | Условия на агрегаты: СУММА(Товары.Сумма) > 0 | Условий на обычные поля — их место в ГДЕ |
ОБЪЕДИНИТЬ ВСЕ | Следующий ВЫБРАТЬ с тем же числом полей; имена колонок берутся из первого запроса | ОБЪЕДИНИТЬ без ВСЕ, если дубли не мешают: удаление повторов стоит времени |
УПОРЯДОЧИТЬ ПО | Поля или псевдонимы, ВОЗР / УБЫВ | Сортировки там, где порядок строк не нужен: она стоит времени |
ИТОГИ … ПО | Агрегаты по псевдонимам полей, список группировок, ОБЩИЕ для общего итога | ОБЩИЕ, если общий итог не нужен: он добавляет уровень в обход |
Проверку результата методом Пустой() до обхода разбираем в статье о пустом результате запроса.
Задача: продажи организации за период по контрагентам и номенклатуре, только строки на сумму не меньше заданной, по убыванию суммы. Документ РеализацияТоваровУслуг с табличной частью Товары есть и в УТ 11, и в БП 3.0. Для аналитики продаж правильнее брать регистры, но на документе нагляднее видны все секции.
// Серверный общий модуль, модуль менеджера или модуль объекта отчета/обработки.
// Имена объектов - как в типовых УТ 11 и БП 3.0; перед запуском сверьте их
// со своей конфигурацией в конфигураторе.
// Возвращает продажи за период в разрезе контрагентов и номенклатуры.
//
// Параметры:
// НачалоПериода - Дата - начало периода.
// КонецПериода - Дата - конец периода (берется до конца дня).
// Организация - СправочникСсылка.Организации - организация-продавец.
// МинимальнаяСумма - Число - в результат попадут строки с суммой не меньше этой.
//
// Возвращаемое значение:
// ТаблицаЗначений - колонки Контрагент, Номенклатура, Количество, Сумма.
//
Функция ПродажиЗаПериод(НачалоПериода, КонецПериода, Организация, МинимальнаяСумма = 0) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Ссылка.Контрагент КАК Контрагент,
| Товары.Номенклатура КАК Номенклатура,
| СУММА(Товары.Количество) КАК Количество,
| СУММА(Товары.Сумма) КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка.Проведен = ИСТИНА
| И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И Товары.Ссылка.Организация = &Организация
|
|СГРУППИРОВАТЬ ПО
| Товары.Ссылка.Контрагент,
| Товары.Номенклатура
|
|ИМЕЮЩИЕ
| СУММА(Товары.Сумма) >= &МинимальнаяСумма
|
|УПОРЯДОЧИТЬ ПО
| Сумма УБЫВ";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("МинимальнаяСумма", МинимальнаяСумма);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Документ.РеализацияТоваровУслуг.Товары — источником служит табличная часть: одна строка результата до группировки — одна строка товаров. Поля шапки берутся через Товары.Ссылка: это неявное соединение с таблицей документа.Товары.Ссылка.Проведен = ИСТИНА отсекает черновики. При пометке на удаление проведение отменяется, поэтому помеченные тоже не попадут.МЕЖДУ &НачалоПериода И &КонецПериода включает обе границы. Параметр конца периода передаётся через КонецДня(): без этого документы последнего дня, записанные после 00:00:00, потеряются.СГРУППИРОВАТЬ ПО перечислены оба поля без агрегатной функции — теми же выражениями, что в ВЫБРАТЬ.ИМЕЮЩИЕ агрегат повторён целиком: псевдоним Сумма здесь ещё недоступен. А в УПОРЯДОЧИТЬ ПО псевдоним уже работает.Когда нужен отчёт вида «контрагент — сумма, под ним товары», удобно добавить секцию ИТОГИ. Она идёт после сортировки и ссылается на псевдонимы полей.
&НаСервере
Процедура ВывестиПродажиПоКонтрагентамНаСервере(НачалоПериода, КонецПериода)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Ссылка.Контрагент КАК Контрагент,
| Товары.Номенклатура КАК Номенклатура,
| СУММА(Товары.Сумма) КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка.Проведен = ИСТИНА
| И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|
|СГРУППИРОВАТЬ ПО
| Товары.Ссылка.Контрагент,
| Товары.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| Сумма УБЫВ
|ИТОГИ
| СУММА(Сумма)
|ПО
| Контрагент";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
// Первый уровень - записи итогов по контрагентам.
ВыборкаКонтрагентов = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаКонтрагентов.Следующий() Цикл
Сообщить(Строка(ВыборкаКонтрагентов.Контрагент) + ": "
+ Формат(ВыборкаКонтрагентов.Сумма, "ЧДЦ=2"));
// Второй уровень - детальные строки этого контрагента.
ВыборкаСтрок = ВыборкаКонтрагентов.Выбрать();
Пока ВыборкаСтрок.Следующий() Цикл
Сообщить(" - " + Строка(ВыборкаСтрок.Номенклатура) + ": "
+ Формат(ВыборкаСтрок.Сумма, "ЧДЦ=2"));
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Обход ОбходРезультатаЗапроса.ПоГруппировкам отдаёт на первом уровне только записи итогов по полю Контрагент, а вложенный Выбрать() — строки внутри текущей группы. Если добавить в ИТОГИ слово ОБЩИЕ, первым уровнем станет общая сумма, а контрагенты уйдут ниже — частая причина «съехавшего» обхода.
Задача: по товарам, которые продавались за период, получить текущий остаток на всех складах. Проданную номенклатуру кладём во временную таблицу, затем соединяем с виртуальной таблицей остатков, а отбор по номенклатуре ставим в её параметры — так остатки считаются только по нужным позициям. Услуги и работы в УТ 11 продаются в той же табличной части Товары, поэтому отсекаем их по типу номенклатуры, иначе они попадут в список с нулевым остатком.
// Серверный общий модуль или модуль менеджера. Пример для УТ 11:
// регистр ТоварыНаСкладах есть в УТ 11, в БП 3.0 его нет.
Функция ОстаткиПроданныхТоваров(НачалоПериода, КонецПериода) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Товары.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_Проданные
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка.Проведен = ИСТИНА
| И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И Товары.Номенклатура.ТипНоменклатуры В
| (ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Товар),
| ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.МногооборотнаяТара))
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Проданные.Номенклатура КАК Номенклатура,
| СУММА(ЕСТЬNULL(Остатки.ВНаличииОстаток, 0)) КАК Остаток
|ИЗ
| ВТ_Проданные КАК Проданные
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
| ,
| Номенклатура В
| (ВЫБРАТЬ
| ВТ.Номенклатура
| ИЗ
| ВТ_Проданные КАК ВТ)) КАК Остатки
| ПО Проданные.Номенклатура = Остатки.Номенклатура
|
|СГРУППИРОВАТЬ ПО
| Проданные.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| Остаток";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
// [0] - результат запроса с ПОМЕСТИТЬ, [1] - остатки.
Результаты = Запрос.ВыполнитьПакет();
Возврат Результаты[1].Выгрузить();
КонецФункции
ВыполнитьПакет() возвращает массив результатов по числу запросов пакета; результат запроса с ПОМЕСТИТЬ содержит не данные, а число помещённых записей. Если нужен только последний результат, хватит Выполнить(). Временная таблица живёт до конца выполнения пакета; чтобы использовать её в другом запросе, назначьте обоим один МенеджерВременныхТаблиц. ЕСТЬNULL нужен, потому что у товара без остатка поле из ЛЕВОГО СОЕДИНЕНИЯ придёт как NULL.
Новый Запрос оказался в клиентской процедуре управляемой формы, а в тонком и веб-клиенте такого типа нет. Перенесите код в процедуру &НаСервере или в серверный общий модуль.ГДЕ или реквизит, которого в вашей конфигурации нет. Имена в УТ, БП и отраслевых решениях отличаются: чужой код сверяйте с деревом метаданных.ВЫБРАТЬ без агрегатной функции и без записи в СГРУППИРОВАТЬ ПО.УстановитьПараметр() или имя параметра в коде не совпадает с &Именем в тексте.В (&Список) или во временную таблицу.Остатки и Обороты отбор по измерениям ставят в параметры таблицы, иначе она сначала рассчитается по всей базе и только потом отфильтруется.Источники — объекты метаданных, а не таблицы СУБД. Есть разыменование ссылок через точку, виртуальные таблицы регистров, секция ИТОГИ и обход результата по группировкам.
Нет. Запрос только читает: вставлять, изменять и удалять записи базы он не умеет, а ПОМЕСТИТЬ и УНИЧТОЖИТЬ работают лишь с временными таблицами. Данные меняют через объекты (ПолучитьОбъект(), Записать()) или наборы записей регистров.
В тонком клиенте и веб-клиенте нельзя: объекта Запрос там нет, из клиентских контекстов он доступен только в толстом клиенте (обычное приложение). Из управляемой формы вызывают серверную процедуру и возвращают на клиент примитивные значения, массивы или структуры; таблицу значений выгружают в реквизит формы прямо на сервере.
Да, у каждого ключевого слова есть английский вариант: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, TOTALS … BY. Имена объектов метаданных остаются такими, как в конфигурации. В типовых решениях принят русский синтаксис.
Если нужен готовый отчёт на СКД или обработка, где запрос собирает данные из нескольких регистров вашей конфигурации, а разбираться в их структуре некогда, задачу быстрее отдать программисту. На 1c-only.ru можно заказать разработку отчётов и обработок 1С или разместить задачу: оценка бесплатная, ответ — в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.