Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Вложенные запросы в 1С
Вложенные запросы (subqueries) — это мощный механизм в SQL, который позволяет встраивать один запрос внутри другого. В 1С вложенные запросы используются для реализации сложной логики выборки данных, фильтрации и аналитики. Это критически важный инструмент для опытного разработчика.
Что это такое
Вложенный запрос — это SELECT внутри другого SELECT, WHERE, FROM или HAVING:
// Синтаксис
СЕЛЕКТ ... ИЗ (
ВЫБРАТЬ ... ИЗ Таблица // Вложенный запрос
) КАК АльиасТаблицы
ГДЕ ...
Типы вложенных запросов
1. Вложенный SELECT в FROM (подзапрос как источник)
Это наиболее часто используемый вид — создание временной таблицы для анализа:
Запрос = Новый Запрос(
"ВЫБРАТЬ
| ПроизводРеестр.Товар,
| ПроизводРеестр.ОбщееКоличество
|ИЗ
| (
| ВЫБРАТЬ // Вложенный запрос
| Товары.Ссылка КАК Товар,
| СУММА(НаличиеТоваров.Количество) КАК ОбщееКоличество
| ИЗ
| РегистрНакопления.НаличиеТоваров КАК НаличиеТоваров
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
| НА НаличиеТоваров.Товар = Товары.Ссылка
| ГРУППИРОВАТЬ ПО
| Товары.Ссылка
| ИМЕЮЩЕЕ
| СУММА(НаличиеТоваров.Количество) > 0
| ) КАК ПроизводРеестр
|ПОРЯДОК ПО
| ПроизводРеестр.ОбщееКоличество УБЫВ");
Результат = Запрос.Выполнить();
Когда использовать:
- Нужны предварительные вычисления (промежуточные суммы)
- Сложная логика фильтрации
- Несколько этапов преобразования данных
- Нужны промежуточные GROUP BY
Преимущества:
- Читаемость (каждый уровень отвечает за свою логику)
- Переиспользование логики
- Лучшая производительность (БД оптимизирует поэтапно)
2. Вложенный SELECT в WHERE (фильтрация по результатам)
Пример: Найти товары, которые продаются лучше среднего
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Товары.Наименование,
| Продажи.КоличествоПроданного
|ИЗ
| РегистрНакопления.ПродажиТоваров КАК Продажи
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
| НА Продажи.Товар = Товары.Ссылка
|ГДЕ
| Продажи.КоличествоПроданного > (
| ВЫБРАТЬ
| СРЕДНЕЕ(Продажи2.КоличествоПроданного) // Подзапрос
| ИЗ
| РегистрНакопления.ПродажиТоваров КАК Продажи2
| )
|ПОРЯДОК ПО
| Продажи.КоличествоПроданного УБЫВ");
Когда использовать:
- Нужно сравнить значение с результатом вычисления
- Фильтрация по условиям из другой таблицы
- Поиск аномалий или выбросов
Проблема — производительность:
// ❌ ПЛОХО — может быть очень медленно
ГДЕ Товар В (
ВЫБРАТЬ Товар ИЗ РегистрНакопления.Продажи
ГДЕ Количество > 100
)
// ✅ ХОРОШО — используй ВНУТРЕННЕЕ СОЕДИНЕНИЕ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (...) НА ...
3. Вложенный SELECT с EXISTS (проверка существования)
Пример: Найти клиентов, которые сделали хотя бы одну покупку
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Клиенты.Наименование
|ИЗ
| Справочник.Клиенты КАК Клиенты
|ГДЕ
| СУЩЕСТВУЕТ ( // EXISTS в 1С
| ВЫБРАТЬ 1 ИЗ Документ.ЗаказКлиента КАК Заказы
| ГДЕ Заказы.Клиент = Клиенты.Ссылка
| )");
Когда использовать:
- Проверка наличия связанных записей
- Поиск главных записей, у которых есть детали
- Фильтрация по наличию документов
Производительность: EXISTS обычно быстрее IN, потому что БД может остановиться после нахождения первого совпадения.
4. Рекурсивные запросы (WITH RECURSIVE)
Пример: Получить всю иерархию подразделений
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Подразделения.Ссылка КАК Подразделение,
| Подразделения.Наименование,
| Подразделения.Родитель,
| 0 КАК ГлубинаИерархии
|ИЗ
| Справочник.Подразделения КАК Подразделения
|ГДЕ
| Подразделения.Родитель = &КорневоеПодразделение // Начало
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Подразделения.Ссылка,
| Подразделения.Наименование,
| Подразделения.Родитель,
| РеквРезультат.ГлубинаИерархии + 1
|ИЗ
| Справочник.Подразделения КАК Подразделения
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ (/* уже построенное дерево */) КАК РеквРезультат
| НА Подразделения.Родитель = РеквРезультат.Подразделение");
Когда использовать:
- Иерархические структуры (org chart, категории)
- Обход графа (кто подчиняется кому)
- Все уровни отношений
Практические примеры из реальной работы
Пример 1: Финансовая аналитика
// Найти товары, у которых маржа выше средней по категории
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Продажи.Товар,
| Продажи.Маржа
|ИЗ
| (
| ВЫБРАТЬ
| РеестрПродаж.Товар,
| Товары.Категория,
| (РеестрПродаж.ЦенаПродажи - РеестрПродаж.СебестоимостьПродажи) /
| РеестрПродаж.ЦенаПродажи * 100 КАК Маржа
| ИЗ
| РегистрСведений.ЦеныИСебестоимость КАК РеестрПродаж
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
| НА РеестрПродаж.Товар = Товары.Ссылка
| ) КАК Продажи
|ГДЕ
| Продажи.Маржа > (
| ВЫБРАТЬ
| СРЕДНЕЕ(МаржаПомощь.Маржа)
| ИЗ
| (...) КАК МаржаПомощь
| ГДЕ
| МаржаПомощь.Категория = Продажи.Категория
| )");
Пример 2: Отслеживание изменений
// Получить последнее изменение для каждого товара
Запрос = Новый Запрос(
"ВЫБРАТЬ
| История.Товар,
| История.ОтДаты,
| История.Цена
|ИЗ
| (
| ВЫБРАТЬ
| Цены.Товар,
| Цены.ОтДаты,
| Цены.Цена,
| РАНГ() ПО (РАЗДЕЛ ПО Цены.Товар ПОРЯДОК ПО
| Цены.ОтДаты УБЫВ) КАК РангЗаписи
| ИЗ
| РегистрСведений.ИсторияЦен КАК Цены
| ) КАК История
|ГДЕ
| История.РангЗаписи = 1");
Оптимизация вложенных запросов
Правило 1: Используй ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо IN
// ❌ Медленно
ГДЕ Товар В (
ВЫБРАТЬ Товар ИЗ РегистрНакопления.Продажи
)
// ✅ Быстро
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи НА Основной.Товар = Продажи.Товар
Правило 2: Кэшируй подзапросы если выполняются много раз
// Если подзапрос одинаковый для всех строк
СредняяЦена = ПолучитьЗначение(
Новый Запрос("ВЫБРАТЬ СРЕДНЕЕ(Цена) ИЗ РегистрСведений.Цены")
).ПолучитьЗначение();
// Затем используй переменную в основном запросе
Запрос.УстановитьПараметр("СредняяЦена", СредняяЦена);
Правило 3: Индексируй поля в подзапросах
- Все поля в ON в JOIN должны быть индексированы
- Поля в WHERE подзапросов должны быть индексированы
- GROUP BY поля должны быть индексированы
Типичные ошибки
- Много вложенности — > 3 уровней вложения тяжело читать
- Подзапрос в SELECT вместо GROUP BY — считает для каждой строки
- Забыли JOIN — используют IN где нужен JOIN
- Циклические зависимости — в рекурсивных запросах
- NULL значения — в подзапросах могут быть NULL, забыли про это
Инструменты отладки
// Вывести SQL запрос для отладки
Сообщить(Запрос.GetQueryText());
// Профилирование в IDE
// Запросы → Анализ запросов → Выполнить с профилированием
Заключение
Вложенные запросы — это мощный инструмент для сложной аналитики и отчётности. Они требуют:
- Понимания SQL синтаксиса
- Умения оптимизировать для производительности
- Знания индексирования
- Навыков тестирования
Профессиональное владение вложенными запросами отличает опытного 1С разработчика от новичка.