Модель запроса

08.08.24

Разработка - Запросы

Работа с текстом запроса напрямую не позволяет просто обратиться к определенному полю, таблице или условию. Однако, работая со схемой, можно добавить к существующему условию новое или вообще включить его в состав другой группы. Как ни странно, в типовых решениях часто можно встретить код работы с текстом запроса наряду с использование схемы. Возможно появление удобной библиотеки или нового объектного подхода позволит отказаться от работы с текстом совсем, а код построения запроса будет аутентичен исходному запросу, но без избыточности и доступный для простой модификации.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Демо-база
.dt 250,23Mb
17
17 Скачать (1 SM) Купить за 1 850 руб.

Оглавление:


Объектная модель запроса


Варианты работы

Поддержка объектной модели запроса позволяет повысить уровень абстракции при работе с запросами SQL. Теперь запрос SQL перестал быть просто текстом, это уже составной объект, в котором можно независимо работать с разными его частями: с источниками и их полями, с отборами и связями, с запросами в составе пакета и их колонками и т.д.

Ниже представлен типовой пример запроса и его объектное представление в разных вариантах:

  1. Программная работа с запросами
  2. РаботаСоСхемойЗапроса
  3. МодельЗапроса

Пример исходного запроса:

ВЫБРАТЬ
    ТоварныеЗапасыОстатки.Номенклатура КАК Товар,
    ТоварныеЗапасыОстатки.МестоХранения КАК Склад,
    ТоварныеЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток,
    ВЫБОР
        КОГДА ТоварныеЗапасыОстатки.КоличествоОстаток > 0
            ТОГДА "Достаточно"
        ИНАЧЕ "Недостаточно"
    КОНЕЦ КАК Состояние
ИЗ
    РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки КАК ТоварныеЗапасыОстатки
ГДЕ
    ТоварныеЗапасыОстатки.МестоХранения = &Склад
УПОРЯДОЧИТЬ ПО
    Товар,
    Склад,
    КоличествоОстаток
ИТОГИ ПО
    ОБЩИЕ,
    Товар,
    Склад

Код формирования схемы, предложенный 1С. Проверить работоспособность кода можно в конструкторе схемы запроса. Для проверки нужно вставить текст примера в поле "Текст модуля", затем вызвать команду "Получить текст запроса": 

СхемаЗапроса = Новый СхемаЗапроса;
Пакет1 = СхемаЗапроса.ПакетЗапросов[0];
Оператор1 = Пакет1.Операторы[0];
// добавим источник в запрос
ТаблицаРегистра = Оператор1.Источники.Добавить("РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки");
// добавим поля в запрос
ПолеТовар = Оператор1.ВыбираемыеПоля.Добавить("ТоварныеЗапасыОстатки.Номенклатура", 0);
ДоступноеПолеСклад = ТаблицаРегистра.Источник.ДоступныеПоля.Найти("МестоХранения");
ПолеСклад = Оператор1.ВыбираемыеПоля.Добавить(ДоступноеПолеСклад, 1);
ПолеКоличество = Оператор1.ВыбираемыеПоля.Добавить("КоличествоОстаток", 2);
ВыражениеВыбора = Новый ВыражениеСхемыЗапроса("ВЫБОР
|КОГДА ТоварныеЗапасыОстатки.КоличествоОстаток > 0
| ТОГДА ""Достаточно""
| ИНАЧЕ ""Недостаточно""
|КОНЕЦ");
ПолеВыбора = Оператор1.ВыбираемыеПоля.Добавить(Строка(ВыражениеВыбора), 3);
// зададим псевдонимы
Результат = Пакет1.Колонки.Найти(ПолеКоличество);
Результат.Псевдоним = "КоличествоОстаток";
Результат = Пакет1.Колонки[1];
Результат.Псевдоним = "Склад";
Результат = Пакет1.Колонки.Найти(ВыражениеВыбора);
Результат.Псевдоним = "Состояние";
// добавим условие
Оператор1.Отбор.Добавить(Строка(ПолеСклад) + " = &Склад");
// добавим упорядочивание
Пакет1.Порядок.Добавить(ПолеТовар);
Пакет1.Порядок.Добавить("Склад");
Пакет1.Порядок.Добавить("КоличествоОстаток");
// зададим контрольные точки итогов
Пакет1.ОбщиеИтоги = Истина;
Пакет1.КонтрольныеТочкиИтогов.Добавить(ПолеТовар);
Пакет1.КонтрольныеТочкиИтогов.Добавить("Склад");
// зададим агрегатные функции для итогов
КолонкаКоличество = Пакет1.Колонки.Найти(ПолеКоличество);
Пакет1.ВыраженияИтогов.Добавить("Сумма(" + КолонкаКоличество.Псевдоним +")");

Формирование схемы с использованием библиотеки РаботаСоСхемойЗапроса:

Перем СхемаЗапроса, ОператорВыбрать;
//  ЗАПРОС ПАКЕТА 0
ЗапросПакета    = РаботаСоСхемойЗапроса.ЗапросПакета(СхемаЗапроса,,, ОператорВыбрать);
РаботаСоСхемойЗапроса.Источник(ОператорВыбрать, "РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки");
ОператорВыбрать.Отбор.Добавить("ТоварныеЗапасыОстатки.МестоХранения = &Склад");
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ТоварныеЗапасыОстатки.Номенклатура", "Товар");
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ТоварныеЗапасыОстатки.МестоХранения", "Склад");
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ТоварныеЗапасыОстатки.КоличествоОстаток");
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ВЫБОР
|   КОГДА ТоварныеЗапасыОстатки.КоличествоОстаток > 0
|       ТОГДА ""Достаточно""
|   ИНАЧЕ ""Недостаточно""
|КОНЕЦ", "Состояние");
РаботаСоСхемойЗапроса.Порядок(ЗапросПакета, "Товар");
РаботаСоСхемойЗапроса.Порядок(ЗапросПакета, "Склад");
РаботаСоСхемойЗапроса.Порядок(ЗапросПакета, "КоличествоОстаток");
РаботаСоСхемойЗапроса.Итог(ЗапросПакета, "Товар", "Товар", );
РаботаСоСхемойЗапроса.Итог(ЗапросПакета, "Склад", "Склад", );
ЗапросПакета.ОбщиеИтоги = Истина;

Формирование схемы через модель запроса. Данный код можно проверить в конструкторе модели запроса по аналогии с конструктором схемы в примере выше:

МодельЗапроса = Общий.МодельЗапроса()
;//  ЗАПРОС ПАКЕТА. Остатки
МодельЗапроса.ЗапросПакета("Остатки")
    .Выбрать()
        .Источник("РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки")
        .Отбор("ТоварныеЗапасыОстатки.МестоХранения = &Склад")
        //  Измерения
        .Поле("ТоварныеЗапасыОстатки.Номенклатура", "Товар")
        .Поле("ТоварныеЗапасыОстатки.МестоХранения", "Склад")
        //  Ресурсы
        .Поле("ТоварныеЗапасыОстатки.КоличествоОстаток")
        .Поле("ВЫБОР
        |   КОГДА ТоварныеЗапасыОстатки.КоличествоОстаток > 0
        |       ТОГДА ""Достаточно""
        |   ИНАЧЕ ""Недостаточно""
        |КОНЕЦ", "Состояние")
    .Порядок("Товар")
    .Порядок("Склад")
    .Порядок("КоличествоОстаток")
    .Группировка("Товар")
    .Группировка("Склад")
    .ОбщиеИтоги()
;

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

Схема запроса, библиотечный подход

Реализованный в платформе объект "Схема запроса" в полной мере представляет объектную модель запроса. У схемы запроса есть один существенный недостаток - его поведение при программном использовании повторяет таковое при интерактивной работе пользователя в конструкторе запросов. Это приводит к неожиданному результату, когда программное описание запроса не соответствует его реальному представлению. Более подробно об этом было написано в моей статье, а также предложено обходное решение на базе использования библиотеки "Работа со схемой запроса".

Еще в схеме запроса сильно не хватает средств для построения описания полей выборки по образцу (объект метаданных, таблица), а также операций по модификации и копирования составляющих объектов схемы. Эти возможности реализованы в библиотеке.

К сожалению, предложенный в платформе объект не избавляет от высокой степени избыточности характерной для текстов запросов. Так при работе с одной и той же таблицей в нескольких запросах требуется повторить ее описание несколько раз. Это приводит к большому объему повторного кодирования, что потенциально затрудняет дальнейшую модификацию и является источником ошибок. Кроме того, в предложенном варианте от 1С в коде используется большое количество промежуточных переменных и магических чисел.

Использование библиотеки "РаботаСоСхемойЗапроса" позволяет получать однозначный результат построения схемы запроса с использованием минимального количества промежуточных переменных текущего состояния. Дополнительные функции построения выборки по образцу (копия оператора, запрос из таблицы) позволяют быстро конструировать схему запроса на основе готовых структур (таблицы значений, коллекции данных формы, табличные части), и, таким образом, избавляют от избыточности. Функции модификации схемы позволяют делать уточнения структуры путем переопределения выражения полей или удаления лишних, замену источников данных, изменение параметров виртуальных таблиц, условий соединения и отборов. Библиотека позволяет генерировать условия связей полей соединения по простому списку или по условию вхождения кортежей исходной таблицы источника в кортежи указанной таблицы. Последнее доступно также для использования в условиях виртуальной таблице или в отборе.

Еще одна возможность библиотеки - сохранение метки на запрос в пакете. Она позволяет обращаться к результату запроса по имени. Эта возможность удобна, когда для обработки данных требуются результаты разных запросов из пакета. В традиционном подходе требуется кодировать обращение к результату по индексу запроса, однако такое решение ненадежно, т.к. изменение текста запроса может привести к изменению индекса результата. Кроме того именованный запрос позволяет внести в текст программы дополнительную семантику.

Работа с библиотекой позволяет существенно сократить объем кодирования и позволяет быстро создавать схемы запросов по шаблонам. А обращение к результату пакета по имени не только вносит дополнительную семантику тексту запроса, но и повышает надежность программы.

Контекст построения схемы запроса

Однако несмотря на все преимущества использовании библиотеки у нее есть существенный недостаток - необходимость работы с контекстом. Разные функции библиотеки требуют передачи разных контекстов: запрос пакета, оператор выборки, источник данных, соединение, поле и даже целиком объект схема запроса. Настоящий контекстный взрыв происходит при попытке описать таким образом построение вложенных запросов. Требование контекста приводит к необходимости использования переменных и к использованию большого количества параметров функций. По сути функции работают с разными состояниями объекта схемы запроса, характеризуемыми объектами контекста.

Пример текста запроса с двумя вложенными подзапросами:

ВЫБРАТЬ
    ВложенныйЗапрос.Товар КАК Товар,
    ВложенныйЗапрос.Склад КАК Склад,
    ВложенныйЗапрос.КоличествоОстаток КАК КоличествоОстаток
ИЗ
    (ВЫБРАТЬ
        ВложенныйЗапрос.Товар КАК Товар,
        ВложенныйЗапрос.Склад КАК Склад,
        ВложенныйЗапрос.КоличествоОстаток КАК КоличествоОстаток
    ИЗ
        (ВЫБРАТЬ
            ТоварныеЗапасыОстатки.Номенклатура КАК Товар,
            ТоварныеЗапасыОстатки.МестоХранения КАК Склад,
            ТоварныеЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток
        ИЗ
            РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки КАК ТоварныеЗапасыОстатки
        ГДЕ
            ТоварныеЗапасыОстатки.МестоХранения = &Склад) КАК ВложенныйЗапрос) КАК ВложенныйЗапрос

Решение задачи построения схемы запроса с вложенными подзапросами при использованием библиотеки РаботаСоСхемойЗапроса:

Перем СхемаЗапроса, ОператорВыбрать;
////////////////////////////////////////////////////////////////////////////////
//  ЗАПРОС ПАКЕТА 0
ЗапросПакета     = РаботаСоСхемойЗапроса.ЗапросПакета(СхемаЗапроса,,, ОператорВыбрать);
ВложенныйЗапросЗапросПакета = РаботаСоСхемойЗапроса.Источник(ОператорВыбрать, РаботаСоСхемойЗапроса.ОписаниеВложенногоЗапроса(), "ВложенныйЗапрос").Источник.Запрос;
//{    Вложенный запрос ВложенныйЗапрос
ВложенныйЗапросОператорВыбрать = РаботаСоСхемойЗапроса.Оператор(ВложенныйЗапросЗапросПакета);
ВложенныйЗапросВложенныйЗапросЗапросПакета = РаботаСоСхемойЗапроса.Источник(ВложенныйЗапросОператорВыбрать, РаботаСоСхемойЗапроса.ОписаниеВложенногоЗапроса(), "ВложенныйЗапрос").Источник.Запрос;
//{    Вложенный запрос ВложенныйЗапросВложенныйЗапрос
ВложенныйЗапросВложенныйЗапросОператорВыбрать = РаботаСоСхемойЗапроса.Оператор(ВложенныйЗапросВложенныйЗапросЗапросПакета);
РаботаСоСхемойЗапроса.Источник(ВложенныйЗапросВложенныйЗапросОператорВыбрать, "РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки");
ВложенныйЗапросВложенныйЗапросОператорВыбрать.Отбор.Добавить("ТоварныеЗапасыОстатки.МестоХранения = &Склад");
РаботаСоСхемойЗапроса.Поле(ВложенныйЗапросВложенныйЗапросЗапросПакета, ВложенныйЗапросВложенныйЗапросОператорВыбрать,, "ТоварныеЗапасыОстатки.Номенклатура", "Товар");
РаботаСоСхемойЗапроса.Поле(ВложенныйЗапросВложенныйЗапросЗапросПакета, ВложенныйЗапросВложенныйЗапросОператорВыбрать,, "ТоварныеЗапасыОстатки.МестоХранения", "Склад");
РаботаСоСхемойЗапроса.Поле(ВложенныйЗапросВложенныйЗапросЗапросПакета, ВложенныйЗапросВложенныйЗапросОператорВыбрать,, "ТоварныеЗапасыОстатки.КоличествоОстаток");
//}    Вложенный запрос ВложенныйЗапросВложенныйЗапрос
РаботаСоСхемойЗапроса.Поле(ВложенныйЗапросЗапросПакета, ВложенныйЗапросОператорВыбрать,, "ВложенныйЗапрос.Товар");
РаботаСоСхемойЗапроса.Поле(ВложенныйЗапросЗапросПакета, ВложенныйЗапросОператорВыбрать,, "ВложенныйЗапрос.Склад");
РаботаСоСхемойЗапроса.Поле(ВложенныйЗапросЗапросПакета, ВложенныйЗапросОператорВыбрать,, "ВложенныйЗапрос.КоличествоОстаток");
//}    Вложенный запрос ВложенныйЗапрос
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ВложенныйЗапрос.Товар");
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ВложенныйЗапрос.Склад");
РаботаСоСхемойЗапроса.Поле(ЗапросПакета, ОператорВыбрать,, "ВложенныйЗапрос.КоличествоОстаток");

Тоже решение с моделью запроса:

МодельЗапроса = Общий.МодельЗапроса()
;//  ЗАПРОС ПАКЕТА. ВложенныйЗапрос
МодельЗапроса.ЗапросПакета("ВложенныйЗапрос")
    .Выбрать()
        .ИсточникНачать("ВложенныйЗапрос")
            .Выбрать()
                .ИсточникНачать("ВложенныйЗапрос")
                    .Выбрать()
                        .Источник("РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки")
                        .Отбор("ТоварныеЗапасыОстатки.МестоХранения = &Склад")
                        .Поле("ТоварныеЗапасыОстатки.Номенклатура", "Товар")
                        .Поле("ТоварныеЗапасыОстатки.МестоХранения", "Склад")
                        .Поле("ТоварныеЗапасыОстатки.КоличествоОстаток")
                .ИсточникЗавершить()
                .Поле("ВложенныйЗапрос.*")
        .ИсточникЗавершить()
        .Поле("ВложенныйЗапрос.*")
;

Сами объекты контекста тоже где то нужно сохранять. На практике для хранения минимального контекста используются переменные: СхемаЗапроса, ОператорВыбрать, СтруктураЗапроса. Но не все состояние хранится в переменных, частично состояние передается в функции по их псевдониму. Так, например, для описания соединения используются псевдонимы источников слева и справа.

Использование переменных само по себе является потенциальным источником ошибок, а необходимость передачи контекста в функции приводит к избыточности в тексте программы. Возможными решениями по избавлению от этих недостатков могут быть: использование для хранения контекста такого объекта как Структура или использование в качестве объекта Обработки.

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

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

Объект "Модель запроса"


Вопрос что делать с контекстом меня мучил уже давно, однако перспектива переписать всю библиотеку занова сдерживала меня. Если идти по пути использования Структуры для сохранения контекста, то переписывание библиотеки неизбежно, т.к. меняется интерфейс функций. Если же использовать Обработку как объект с инкапсуляцией состояния, то можно переиспользовать библиотеку за фасадом методов.

Удачной иллюстрацией подхода с инкапсуляцией состояния для меня стала публикация "Новый способ программной настройки условного оформления". Чем больше я думал над этим примером, тем больше перспектив я видел при использовании Обработок в качестве объектов с инкапсуляцией в приложении к различным задачам. И хорошим поводом попробовать новый подход стала реализация модели запроса.

Разработка модели потребовала относительно небольших усилий, так как является по сути фасадом над уже готовой библиотекой. Потребовалось реализовать методы фасада и полностью переписать конструктор модели.

Фасад модели был расширен за счет реализации части настроек схемы через вызов методов. Например для установки автопорядка был добавлен метод Автопорядок(), установки параметра виртуальной таблицы Период - Период("Месяц") и т.д. Использование методов для установки свойств позволяет не прерывать интерфейс вызова, реализуя текучий интерфейс.

В обработке интерактивного конструктора реализована модель построения кода в стиле текучего интерфейса. Текучий интерфейс позволяет избавиться от избыточного повторения имени объекта контекста и полностью сосредоточится на выполнении последовательности методов. Фактически через него реализуется DSL по построению модели запроса.

Примеры работы с моделью


Примеры для работы с моделью вы можете получить для своих запросов самостоятельно используя обработку "Конструктор модели запроса". Порядок работы с обработкой аналогичен из поставки библиотеки "Работа со схемой запроса".

Некоторые неочевидные возможности я рассмотрю подробнее. Для начала взгляните на пример текста запроса и вариант построения модели:

Пример текста запроса:

ВЫБРАТЬ
    Таблица.Номенклатура КАК Номенклатура,
    Таблица.Организация КАК Организация
ПОМЕСТИТЬ ВТ_ИЗМЕРЕНИЯ
ИЗ
    &Таблица КАК Таблица
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ИЗМЕРЕНИЯ.Номенклатура КАК Номенклатура,
    ВТ_ИЗМЕРЕНИЯ.Организация КАК Организация,
    ЕСТЬNULL(ТоварныеЗапасыОстатки.МестоХранения, ЗНАЧЕНИЕ(Справочник._ДемоМестаХранения.ПустаяСсылка)) КАК Склад,
    ЕСТЬNULL(ТоварныеЗапасыОстатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    ВТ_ИЗМЕРЕНИЯ КАК ВТ_ИЗМЕРЕНИЯ
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки(, (Организация) В (ВЫБРАТЬ РАЗЛИЧНЫЕ Организация ИЗ ВТ_ИЗМЕРЕНИЯ КАК ВТ_ИЗМЕРЕНИЯ)) КАК ТоварныеЗапасыОстатки
        ПО (ВТ_ИЗМЕРЕНИЯ.Организация = ТоварныеЗапасыОстатки.Организация
                И ВТ_ИЗМЕРЕНИЯ.Номенклатура = ТоварныеЗапасыОстатки.Номенклатура)
УПОРЯДОЧИТЬ ПО
    Номенклатура,
    Организация,
    Склад
АВТОУПОРЯДОЧИВАНИЕ

Пример кода построения модели запроса с последующей обработкой результата запроса. Для проверки примера уберите секцию обработки результата или скопируйте текст в консоль кода:

    Таблица = РегистрыНакопления._ДемоОстаткиТоваровВМестахХранения.СоздатьНаборЗаписей().ВыгрузитьКолонки("Организация,Номенклатура");
    МодельЗапроса = Общий.МодельЗапроса()
    ;//  Запрос пакета. ВТ_ИЗМЕРЕНИЯ
    МодельЗапроса.ЗапросПакета().Поместить("ВТ_ИЗМЕРЕНИЯ")
        .Источник(Таблица)
        .Поле("*")
    ;//  ЗАПРОС ПАКЕТА. Остатки
    МодельЗапроса.ЗапросПакета("Остатки")
        .Выбрать()
            .Источник("ВТ_ИЗМЕРЕНИЯ")
            .Источник("РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки")
                .УсловиеВхождения("ВТ_ИЗМЕРЕНИЯ", "Организация")
            .ЛевоеСоединение("ВТ_ИЗМЕРЕНИЯ", "ТоварныеЗапасыОстатки")
                .Связь("Организация, Номенклатура")
            .Поле("ВТ_ИЗМЕРЕНИЯ.*")
            .Поле("ТоварныеЗапасыОстатки.МестоХранения", "Склад", "ЗНАЧЕНИЕ(Справочник._ДемоМестаХранения.ПустаяСсылка)")
            .Поле("ТоварныеЗапасыОстатки.КоличествоОстаток", "Остаток", "0")
        .Порядок("Номенклатура")
        .Порядок("Организация")
        .Порядок("Склад")
        .Автопорядок()
    ;//  Обработка результата
    Сообщить(МодельЗапроса.ПолучитьТекстЗапроса());
    МодельЗапроса.ВыполнитьЗапрос();
    РезультатЗапроса = МодельЗапроса.Результат("Остатки");

Временная таблица из параметра по образцу

В следующем примере представлена возможность описания запроса выборки временной таблицы из таблицы значений. Сама выборка определяется в описании источника: вначале указано имя параметра (в обычном варианте здесь указывается имя таблицы), затем псевдоним и последним параметром - сама таблица значений (Табличная часть, Коллекция данных формы, Колонки).

На этапе построения схемы запроса таблица значений служит лишь образцом, необходимым для определения состава и типа колонок временной таблицы. В качестве образца могут быть следующие типы: Таблица значений, Коллекция данных формы, Табличная часть, Строка колонок через запятую, Массив или Колонки. Сами поля при этом перечислять не обязательно, достаточно использовать краткую форму - "*".

Описание источника временной таблицы:

    .Источник(Таблица)
    .Поле("*")
;

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

Описание в качестве источника вложенного запроса является хорошей иллюстрацией, когда контекст, рекурсивно повторяющийся для каждого подзапроса, скрыт в модели.

Технически текущий контекст для каждого уровня вложенности сохраняется на стеке в операторе ИсточникНачать. По окончании описания подзапроса оператором ИсточникЗавершить из стека восстанавливается предыдущий контекст модели.

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

Рекурсивное описание подзапросов:

    .ИсточникНачать("ВложенныйЗапрос")
        .Выбрать()
            .ИсточникНачать("ВложенныйЗапрос")
                .Выбрать()
                    .Источник("РегистрНакопления._ДемоОстаткиТоваровВМестахХранения.Остатки", "ТоварныеЗапасыОстатки")
                    .Отбор("ТоварныеЗапасыОстатки.МестоХранения = &Склад")
                    .Поле("ТоварныеЗапасыОстатки.Номенклатура", "Товар")
                    .Поле("ТоварныеЗапасыОстатки.МестоХранения", "Склад")
                    .Поле("ТоварныеЗапасыОстатки.КоличествоОстаток")
            .ИсточникЗавершить()
            .Поле("ВложенныйЗапрос.*")
    .ИсточникЗавершить()
    .Поле("ВложенныйЗапрос.*")
;

Метка

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

Определение метки на запроса и использование ее для получения результата:

МодельЗапроса.ЗапросПакета("Остатки");
…
РезультатЗапроса = МодельЗапроса.Результат("Остатки");

Параметры виртуальной таблицы

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

Соединение, связь

Обычно условие соединения описывается как связь полей таблицы слева и справа. Для быстрого описания такого рода условий используется оператор Связь, в параметры которого передается список полей. Если поля различаются, то указывается выражение вида "Поле слева = Поле справа", иначе просто достаточно указать Имя поля или полей через запятую.

Пример использования оператора Связь по полям Организация, Номенклатура:

    .ЛевоеСоединение("ВТ_ИЗМЕРЕНИЯ", "ТоварныеЗапасыОстатки")
        .Связь("Организация, Номенклатура")

Оператор условия: Условие, УсловиеСвязи, Отбор

Следущие операторы задают условия для разных контекстов. Оператор "Условие" относится к параметру виртуальной таблицы, "УсловиеСвязи" - условию соединения из секции ПО, "Отбор" - условию секции ГДЕ.

Расширение оператора условия - "%ОператорУсловия%Вхождения"

В данном примере используется расширение оператора Условие как УсловиеВхождения (см. аналоги: СвязьВхождения, ОтборВхождения - для задания условия соединения и секции ГДЕ соответственно). Такого вида условие предполагает следующий текст запроса:

"(%1) В (ВЫБРАТЬ РАЗЛИЧНЫЕ %2 ИЗ %3 КАК %3)”, где 1 - поля таблицы слева, 2 - поля таблицы справа, 3 - псевдоним таблицы справа.

Текст запроса вхождения:

    (Организация) В (ВЫБРАТЬ РАЗЛИЧНЫЕ Организация ИЗ ВТ_ИЗМЕРЕНИЯ КАК ВТ_ИЗМЕРЕНИЯ)

Оператор вхождения для параметра виртуальной таблицы (СвязьВхождения, ОтборВхождения - для соединения и секции ГДЕ соответственно):

    .УсловиеВхождения("ВТ_ИЗМЕРЕНИЯ", "Организация")

Поле, ЕстьNull, Агрегатные функции, Автономер

Описание полей указывается после описания источника, из которого эти поля могут быть выбраны. Формат оператора поля допускает опускать псевдоним, тогда псевдоним будет сгенерирован по-умолчанию. Также для поля допускается указать null значение в третьем параметре.

Выражение оператора эквивалентно тексту запроса ЕСТЬNULL(ТоварныеЗапасыОстатки.КоличествоОстаток, Остаток,  0):

    .Поле("ТоварныеЗапасыОстатки.КоличествоОстаток", "Остаток", "0")
;

Для использования агрегатных функций можно использовать предопределенные операторы: Сумма, Количество, КоличествоРазличных, Максимум и Минимум. При этом система автоматически сформирует состав полей группировки.

И на последок - оператор Автономер(Псевдоним), который добавляет поле с функцией АВТОНОМЕРЗАПИСИ().

Обработка результата

Для получения результата запроса необходимо: установить параметры, выполнить запрос, обратиться к результату запроса.

Установка параметра - таблица значений, выполнение запроса, обращение к результату запроса по метке:

МодельЗапроса
    .Параметр("Период", ТекущаяДата())
;
МодельЗапроса.ВыполнитьЗапрос();
РезультатЗапроса = МодельЗапроса.Результат();

Обработка "Конструктор модели запроса"


Интерактивная обработка позволяет генерировать код модели на основе текста запроса. Описание работы аналогично с Обработка "Конструктор схемы запроса".

Состав и установка


Состав

  • общий модуль библиотеки РаботаСоСхемойЗапроса
  • обработка МодельЗапроса, реализующая объектный интерфейс
  • Внешняя обработка "Конструктор модели запроса" (отдельный проект)
  • Внешняя обработка "Конструктор схемы запроса" (поставляется как есть)

Установка

Объединить с файлом конфигурации из Демо-базы:

Зависимости


Проект разрабатывается на EDT и выложен в общий доступ на github. Текущая версия платформы 1С 8.3.24.

Схема запроса запрос модели SQL текучий интерфейс

См. также

SALE! 15%

Инструментарий разработчика Роли и права Запросы СКД Программист Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    159389    872    399    

861

Запросы Программист Бесплатно (free)

Увидел cheatsheet по SQL и захотелось нарисовать подобное, но про запросы.

18.10.2024    9869    sergey279    18    

64

Запросы Программист Платформа 1С v8.3 Запросы Конфигурации 1cv8 Бесплатно (free)

Столкнулся с интересной ситуацией, которую хотел бы разобрать, ввиду её неочевидности. Речь пойдёт про использование функции запроса АВТОНОМЕРЗАПИСИ() и проблемы, которые могут возникнуть.

11.10.2024    5163    XilDen    36    

80

Запросы Программист Запросы Бесплатно (free)

Отлаживая взаимодействие с базой данных, мы регулярно сталкиваемся с зависающими или подозрительно долго выполняющимися обращениями, негативно влияющими на производительность. О том, как в PostgreSQL выявить подозрительные запросы, основываясь на доступной о них информации, расскажем в статье.

16.08.2024    7897    user1840182    5    

28

Математика и алгоритмы Запросы Программист Платформа 1С v8.3 Запросы Бесплатно (free)

Рассмотрим быстрый алгоритм поиска дублей с использованием hash функции по набору полей шапки и табличных частей.

08.07.2024    2393    ivanov660    9    

22

Запросы СКД Программист Стажер Система компоновки данных Россия Бесплатно (free)

Часто при разработке отчетов в СКД возникает ситуация, когда не совсем понятно, почему отчет выводит не те данные, которые нужны, либо не выводит вовсе. Возникает потребность увидеть конечный запрос, который формирует СКД. Как это сделать, рассмотрим в этой статье.

15.05.2024    8684    implecs_team    6    

47

Запросы Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    3391    andrey_sag    10    

36
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. artbear 1563 01.03.21 11:32 Сейчас в теме
Хороший результат получается с текучим интерфейсом.

проще и нагляднее.

есть небольшие приятные фишки - не нужно указывать поля таблицы-параметра, поля вложенной таблицы

но главный вопрос так и остается - в чем плюсы подобного построения запроса перед непосредственным текстом запроса?
3. kalyaka 1105 01.03.21 12:05 Сейчас в теме
(1)Преимущество в объектной модели запроса проявляется тогда, когда текст запроса получается алгоритмическим образом. Обычно это требуется для динамических запросов, что-то типа СКД, но не для отчетов, а для работы с данными. Например в этой публикации есть решение для параметризированных отборов данных, где требуется "подхватить" исходный запрос из динамического списка и добавить к нему дополнительные условия через соединения или отбор.

Еще это может пригодится в алгоритмах, которые решают разные задачи, но применяются последовательно к тексту запроса. Такие алгоритмы работают по типу конвейера, однако отдельные части конвейера могут быть пропущены в зависимости от условий. Работают эти алгоритмы с абстрактной структурой запроса. Например это может быть оборачивание условий в запросе в некоторую внешнюю группу условий. Или это могут быть алгоритмы работы с такими абстракциями как операции на множествами (пересечение, объединение, исключение).

Даже статические запросы имеет смысл строить через объектную модель. Во первых решение может развиваться и статический запрос вероятно будет меняться, во вторых сложные запросы в алгоритмах можно представить семантически в более простом виде. Запрос, построенный алгоритмически лучше рефакторится, т.к. здесь нет ограничений, накладываемых синтаксисом построения текста запроса.
jan-pechka; +1 Ответить
2. karpik666 3851 01.03.21 11:55 Сейчас в теме
Спасибо, за подробное описание. Однако схема запроса подходит для мелкой доработки типовых запросов, чтобы не лезть в сам текст запроса. Если же сам текст формируется "с нуля" через схему запроса. то периодически поддерживать такую разработку - это ад.
sml; Bassgood; +2 Ответить
4. kalyaka 1105 01.03.21 12:24 Сейчас в теме
(2)На самом деле я сторонник именно построения с нуля, чем модификации. Дело в том, что при построении запроса видна последовательность, а когда модифицируется существующий запрос, то могут быть очень не очевидные последствия. Например удаление поля в первом запросе может изменить текст запроса по всем запросам пакета. Для точечных исправлений предложенное здесь решение врятли будет хорошо работать. Тут больше подойдет слоган: "хочешь изменить запрос - построй его заново" :) Ну или можно достроить
5. karpik666 3851 01.03.21 12:31 Сейчас в теме
(4) вы используете такой вариант построения в командной разработке? Самое забавное начинается, когда схему просят модифицировать через год после реализации задачи.
6. kalyaka 1105 01.03.21 14:44 Сейчас в теме
(5)Использую. То что вы пишете можно к любому плохо написанному коду отнести. Вопрос в том, какой сделать выбор сейчас, что бы потом не было так больно🙂 А вся проблема в том, что программирование неразрывно связано со сложностью моделирования объекта предметной области. И здесь по любому будет компромисс. Одним из действенных способов борьбы со сложностью является метод абстракций. Однако палка о двух концах🙂

Вот вам пример. Нужно инициализировать массив числами от 1 до 10. Какой вариант вы предпочтете: поэлементная инициализация или функция с параметрами начала и конца? В приложении к тексту запроса примерно тот же выбор.
7. karpik666 3851 01.03.21 15:01 Сейчас в теме
(6) пример не подходит, потому что не применим к реальным задачам, не припомню ни разу, чтобы требовался массив чисел. Идея я вашу понял, но такая абстракция только усложняет понимание, что конкретно делает запрос, да и последующий рефакторинг тоже, также странно, что подход вы относите к хорошо написанному коду. Мне больше понятна абстракция, которая сделана например в конфигурации ЗУП 3, когда ты вызываешь функцию с параметрами, и получаешь итоговый запрос, таким образом разработчику не нужно знать, где именно расположены данные, а только знать, какие данные ты можешь получить.
Bassgood; +1 Ответить
8. kalyaka 1105 01.03.21 15:31 Сейчас в теме
(7)Практический пример. Делаете вы обработку со статическим запросом и с параметрами. Через какое-то время оказывается, что у запроса переменное число параметров. Параметры не только в секции ГДЕ, но и в параметрах виртуальной таблицы. Создавать запрос через склейку текста или обратиться к его модели?

Кстати я посмотрел как там устроено в ЗУПе. Я об этом писал в статье, что в типовых решениях схема запроса используется наравне с работой с текстом. Текст используется в качестве шаблонов для быстрой инициализации схемы. Так что компромисс такой: шаблоны в виде текста, построение запроса с помощью схемы.
9. karpik666 3851 01.03.21 15:40 Сейчас в теме
(8) чтобы запрос доработать, надо понять, что конкретно он делает, обычно задача поступает, когда либо ты ее не разрабатывал с нуля, либо так давно, что уже и не помнишь, и какое-то время нужно уделить на чтение уже созданного кода. Плюс такое изменение параметров довольно частный случай, возможно понадобится полностью переписать запрос, так как целиком поменялась логика работы, и в таком случае гораздо удобней использовать именно конструктор запросов, а не смотреть на его абстракцию.

Кстати я посмотрел как там устроено в ЗУПе. Я об этом писал в статье, что в типовых решениях схема запроса используется наравне с работой с текстом. Текст используется в качестве шаблонов для быстрой инициализации схемы. Так что компромисс такой: шаблоны в виде текста, построение запроса с помощью схемы.

Да, там используется схема запросов, однако для разработчика, это черный ящик, он не должен задумываться, как там все работает, в него закидываются параметры, а на выходе получается итоговый текст запроса, с таким же успехом можно динамически строить запрос, склеивая из кусков текста, конечный программист все равно это не увидит.
Bassgood; +1 Ответить
26. jan-pechka 426 04.12.22 11:49 Сейчас в теме
(2)
то периодически поддерживать такую разработку - это ад.


в типовом решении ДиетПитание как раз намудрено с удалить/добавить временную таблицу:

	МассивЗапросов = Новый Массив;
	ШаблонЗапросаВыборка = "ВЫБРАТЬ
	|	ТаблицаДанных.*
	|ИЗ
	|	%1 КАК ТаблицаДанных
	|%2";
	ШаблонЗапросаУдалить = "УНИЧТОЖИТЬ %1";


//полный текст процедуры ниже из модуляОбъекта Питание_Калькуляция:

Процедура ПеренестиВременныеТаблицыВОбъект(ДанныеРасчетов, СтрокаПереносимыхТаблиц = "")
	
	Если ПустаяСтрока(СтрокаПереносимыхТаблиц) Тогда
	
		МассивТаблиц = ОбщегоНазначения.СкопироватьРекурсивно(ДанныеРасчетов.ТаблицыДляПерезаполнения);
	
	Иначе
	
		МассивТаблицБезОтбора = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(СтрокаПереносимыхТаблиц, ",");
		МассивТаблиц = Новый Массив;
		
		Для каждого ИмяТаблицы Из МассивТаблицБезОтбора Цикл
		
			Если ДанныеРасчетов.ТаблицыДляПерезаполнения.Найти(ИмяТаблицы) = Неопределено Тогда
				
				УдалитьТаблицу(ДанныеРасчетов, ИмяТаблицы);
				Продолжить;
			
			КонецЕсли; 
			
			МассивТаблиц.Добавить(ИмяТаблицы);
		
		КонецЦикла; 
	
	КонецЕсли; 

	Если МассивТаблиц.Количество() = 0 Тогда
	
		Возврат;
	
	КонецЕсли; 
	
	МассивЗапросов = Новый Массив;
	ШаблонЗапросаВыборка = "ВЫБРАТЬ
	|	ТаблицаДанных.*
	|ИЗ
	|	%1 КАК ТаблицаДанных
	|%2";
	ШаблонЗапросаУдалить = "УНИЧТОЖИТЬ %1";
	
	МассивЗапросов = Новый Массив;
	
	Для каждого ИмяТаблицы Из МассивТаблиц Цикл
		
		ТекстСортировки = ПолучитьТекстСортировкиТаблицы(ДанныеРасчетов, ИмяТаблицы, "ТаблицаДанных");
	    ТекстЗапроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонЗапросаВыборка, ИмяТаблицы, ТекстСортировки);
		МассивЗапросов.Добавить(ТекстЗапроса);
	
	КонецЦикла; 
	
	Для каждого ИмяТаблицы Из МассивТаблиц Цикл
		
	    ТекстЗапроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонЗапросаУдалить, ИмяТаблицы);
		МассивЗапросов.Добавить(ТекстЗапроса);
	
	КонецЦикла; 
	
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = ДанныеРасчетов.МенеджерВременныхТаблиц;
	Запрос.Текст = СтрСоединить(МассивЗапросов, Символы.ПС + ";" + Символы.ПС);
	
	УстановитьПривилегированныйРежим(Истина);
	РезультатыЗапросов = Запрос.ВыполнитьПакет();
	УстановитьПривилегированныйРежим(Ложь);
	
	Счетчик = 0;
	
	Для каждого ИмяТаблицы Из МассивТаблиц Цикл
		
		ЗаполняемаяТаблица = ЭтотОбъект[ИмяТаблицы];
		ЗаполняемаяТаблица.Очистить();
		РезультатЗапроса = РезультатыЗапросов[Счетчик];
		
		Счетчик = Счетчик + 1;
		
		Питание_НаКлиентеНаСервере.УдалитьЗначениеИзМассива(ДанныеРасчетов.СуществующиеВременныеТаблицы, ИмяТаблицы);
		Питание_НаКлиентеНаСервере.УдалитьЗначениеИзМассива(ДанныеРасчетов.ТаблицыДляПерезаполнения, ИмяТаблицы);
		
		Если РезультатЗапроса.Пустой() Тогда
		
			Продолжить;
		
		КонецЕсли; 
		
		ВыборкаЗапроса = РезультатЗапроса.Выбрать();
		
		Пока ВыборкаЗапроса.Следующий() Цикл
		
			НоваяСтрока = ЗаполняемаяТаблица.Добавить();
			ЗаполнитьЗначенияСвойств(НоваяСтрока, ВыборкаЗапроса);
		
		КонецЦикла; 			
		
	
	КонецЦикла; 
	
КонецПроцедуры
Показать


п.с. это реально- ад, чтобы разобраться в этом...отладчиком еще реально прочитать, а вот чтоб найти КУДА вставить свою дописку с новым полем, отражаемым на форме-это кошмар!
10. JohnyDeath 302 01.03.21 17:40 Сейчас в теме
Очень красиво, лаконично и удобно получилось. Спасибо!
Опробуем в бою
11. Yashazz 4790 02.03.21 17:34 Сейчас в теме
Ну, сомнительна мне польза таких экзерсисов... Типовые всё равно идут по пути строкового конструирования и запихивания во временные, с передачей менеджера ВТ. Поэтому для типовых всё это мало применимо. А свои тем более проще текстом - так оно нагляднее, чем любой код.
sml; Darklight; zqzq; +3 Ответить
12. Ish_2 1112 02.03.21 23:19 Сейчас в теме
(11)
Ну, сомнительна мне польза таких экзерсисов...


Автор в (3) уже обосновал применение "СхемыЗапроса".
Я бы добавил пример когда без "СхемыЗапроса" никак не обойтись.

Допустим, в текущей конфигурации 1с мы должны обрабатывать произвольные запросы к данным ( в синтаксисе запросов 1с) ,поступающие из других приложений 1с или НЕ 1с, используя http-сервис.
Тогда процедура общего модуля в качестве входных параметров имеет :
- строковую переменную "ТекстЗапроса"
- соответствие "ПараметрыЗапроса".
Внутри этой процедуры мы не можем сразу написать :
Запрос= Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.Выполнить()
Нам нужно как минимум перед выполнением проверить "ТекстЗапроса" на синтаксис.
Далее , используя объект "СхемаЗапроса" , возможны некоторые проверки и исправления текста запроса.
( ну, например, у меня в конкретном решении удаляются из запроса поля выборки с типами "ДвоичныеДанные" ,"Тип")

Без объекта "СхемаЗапроса" такая функциональность была бы невозможна.
14. Darklight 33 04.03.21 17:21 Сейчас в теме
Данный комментарий основан на очень давнем моём опыте знакомства со Схемой запроса – а появилась она очень давно, и я тогда возлагал на неё очень большие надежды.
Насколько я знаю "СхемаЗапроса" - та ещё дрянь (пробовал пользоваться года три-четыре назад, тогда желание дальше пользоваться быстро пропало - у данного инструмента пять фундаментальных проблем:
1. Очень ограниченный API на изменения – банально даже поиска нормального даже в линейном списке нет (сейчас – смотрю, что некоторые средства поиска всё-таки появились), не говоря уже о поиске нужной точки изменения во всём запрос (сейчас искать можно только через глубокий рекурсивный перебор всех узлов) - от того конструкции работы с этим объектом просто монструозные - с кучей вложенных циклов или с рекурсией (сейчас, с появление методов «Найти» и «НайтиПоПсевдониму» - правда не везде, и не так как хотелось бы (поиск в глубину, поиск всех, сложно-составной поиск), циклов стало чуть поменьше); и даже если создавать свой хелперский API - всё равно – все банальные операции выливаются в десятки строк кода. В 98% гораздо проще просто обработать текст банальными текстовым функциями. А надёжность - да почти не пострадает (будет на уровне 50% в обоих случаях) - тут просто проблемы уже глубже лежат - внутри самого синтаксиса запросов. API очень нелогичный и запутанный – в нём сложно разбираться и применять его (всё гораздо хуже чем API СКД).

2. Работать можно только очень строго последовательно - т.е. если надо добавить поле - то нужно сначала добавить источник этого поля (если его нет), а чтобы его добавить - нужно добавить все прочие источники, от которых он зависит, и настроить соединения (при этом нельзя переносить готовые источники из других схем). А когда доходит до временных таблиц – так вообще просто жесть какая-то при работе выходит(сейчас, смотрю, тут многое доработали, но всё-равно всё выглядит очень топорно и неудобно).

3. Шаг влево - шаг в право - и всё - сразу ошибка при вызове метода (было бы куда удобнее – если бы встроенный вариатор терпимее относился к ещё недостроенным/неоткорректированным тексам запросов, оставляя проверку хотя бы до момента получения итогового текста запроса). От того вносить правки в запросы - ОЧЕНЬ сложно и громоздко - приходится думать минимум на три шага вперёд и "бегать" по всему API данного объекта и его производных. От того код модификации получается крайне запутанным и трудно воспринимаемым. Ну, а если исходный запрос вообще ещё требует постобработки (да хотя бы банальной сборки из нескольких частей-подзапросов и временных таблиц, а в конфигурациях такое бывает частенько) - то в схему его либо вообще не загрузить, либо если и загрузить - то по выходу потеряются все "управляющие комментарии" (или "фигурные" расширения языка запросов - да хоть для СКД).

4. Если к с запросу написан алгоритм модификации его через схему запроса. А потом у исходного запроса изменится структура – банально – добавиться ещё одна выборка «объединить», или новые поля, или ещё одно вложение – то с большой долей вероятности алгоритм модификации такого запроса перестанет работать (ибо вряд ли его будут составлять так – чтобы он аккуратно рекурсивно выискивал все места, где нужна модификация, строго по именам/выражениям(а уж если изменятся части выражений – то вообще труда) – скорее всего будет банальная адресация по числовым индексам). Да, если запрос модифицируется текстово – то он тоже может перестать работать – но вероятность этого, как я считаю (при правильном подходе к модификации текста) – куда ниже (было бы ещё куда ниже – если бы конструктор запроса позволял больше возможностей по вставке точек интеграции и не удалял псевдонимы в объединяемых запросах).

5. Доступность: "Сервер, толстый клиент, внешнее соединение". Может не так уж принципиально, но порой тексты запросов формируются на клиенте, а потом только передаются на сервер для выполнения (или устанавливаются в интерактивные объекты)

А проблема доработки самих запросов куда глубже находится - 1С просто не тем путём их развивает. Была когда-то неплохая идея - в виде "ПостроителяЗапросов" - её бы развить до чего-то более продвинутого, да применять везде, где работа с запросами - вот и не пришлось бы вручную тексты запросов править. Запросы должны изначально быть как можно более декларатативными, и опциональными - чтобы ими можно было целенаправленно программно управлять извне - причём с отложенной финальной сборкой - до момента пока все команды настройки не будут применены (которые, к слову, вообще могут располагаться в разных местах, да хоть в разных расширениях и подписках, и просто применяться последовательно пока запрос не дойдёт до финальной стадии выполнения).

Простой пример 1: Модифицируем запрос из временной таблицы (ТЗ) объединение с другим запросом из временной таблицы (ТЗ2)
Текст = "ВЫБРАТЬ ТЗ.А ИЗ ТЗ КАК ТЗ"; //Раньше бы такой текст не прокатил бы, сейчас работает
	Сз = Новый СхемаЗапроса();
	Сз.УстановитьТекстЗапроса(Текст);
	Пз = Сз.ПакетЗапросов[0];
	Оператор = Пз.Операторы.Добавить();
	Оператор.ТипОбъединения = ТипОбъединенияСхемыЗапроса.ОбъединитьВсе;
	ист = Оператор.Источники.Добавить(Тип("ОписаниеВременнойТаблицыСхемыЗапроса"),"ТЗ2","ТЗ").Источник;
	ист.ДоступныеПоля.Добавить("А"); //Вот так нужно определять все поля временной таблицы вне схемы (заранее создать отдельно «ОписаниеВременнойТаблицыСхемыЗапроса» нельзя – нет конструктра)
ист.ДоступныеПоля.Добавить("Б"); //Ещё одно поле для второго примера
	Оператор.ВыбираемыеПоля.Добавить("ТЗ.А");
Показать

А теперь если нужно сделать агрегацию этих запросов по полю «А»:
Пз2 = Сз.ПакетЗапросов.Добавить(Тип("ЗапросВыбораСхемыЗапроса"));
	Оператор = Пз2.Операторы.Добавить();
	Вложеный = Оператор.Источники.Добавить(Тип("ВложенныйЗапросСхемыЗапроса"),"Вложенный").Источник.Запрос;
	Вложеный.Операторы.Удалить(0); //Удалим автоподарочек - пустой оператор - только тут он не удалится (сначала надо добавить другие операторы)
	Вложеный.Операторы.Добавить(Пз.Операторы[0]); //Спасибо за возможность копирования, ранее её не было
	Вложеный.Операторы.Добавить(Пз.Операторы[1]);
	Вложеный.Операторы.Удалить(0); //Удалим автоподарочек - пустой оператор
	Оператор.ВыбираемыеПоля.Добавить("СУММА(Вложенный.А)");//Не знаю как тут добавить псевдоним поля
	Пз2.Операторы.Удалить(0); //Удалим автоподарочек - пустой оператор
	Сз.ПакетЗапросов.Удалить(0);
	Текст = Сз.ПолучитьТекстЗапроса();
	
	//Получим вот такой запрос
	Текст  = "
	         |ВЫБРАТЬ
	         |	СУММА(Вложенный.А) КАК А
	         |ИЗ
	         |	(ВЫБРАТЬ
	         |		ТЗ.А КАК А
	         |	ИЗ
	         |		ТЗ КАК ТЗ
	         |	
	         |	ОБЪЕДИНИТЬ ВСЕ
	         |	
	         |	ВЫБРАТЬ
	         |		ТЗ.А
	         |	ИЗ
	         |		ТЗ2 КАК ТЗ) КАК Вложенный";
Показать

Вот столько запутанных операторов надо для простого действия – а если нужно собрать запрос на сотни строк (про тысячи я говорить не буду – это уже сложная автоматизация, а не ручные модификации) из десятков таблиц?
Был бы нормальный API – то можно было бы сделать так (не претендую на самый удачный API – просто набросал на скорую руку)
КСЗ_1 = Новый КонструкторСхемыЗапроса("ВЫБРАТЬ ТЗ.А ИЗ ТЗ КАК ТЗ");
	КСЗ_2 = КСЗ_1.Скопировать();
	КСЗ_2.Пакеты[0].Операторы[0].Таблицы[0].Источник.ПутьКДанным= "ТЗ2";
	КСЗ = Новый КонструкторСхемыЗапроса("ВЫБРАТЬ СУММА(Вложенный.А) КАК А"); //Игнорирует, что источника "Вложенный" ещё нет
	ВложеннаяТаблица = КСЗ.Пакеты[0].Выборка[0].Таблицы.Добавить(Тип("ВложенныйЗапросСхемыЗапроса"),"Вложенный"));
	ВложеннаяТаблица.Выборка.Добавить(КСЗ_1.Пакеты[0].Операторы[0]);
	ВложеннаяТаблица.Выборка.Добавить(КСЗ_2.Пакеты[0].Операторы[0]);
	Текст = КС3.ПолучитьТекстЗапроса();
Показать


Простой пример 2: Нужно в готовый запрос добавить таблицу («ЗначенияСвойствОбъектов») левым соединением и выбрать из неё поле значения (за основу возьмём предыдущий итоговый запрос, но левое соединение и выбор поля нужно добавить только к выборке из таблицы ТЗ2 – а в ТЗ1 должно быть NULL, и поле нужно пробросить вверх – сделав по нему группировку). Но только в том, случае – если этой таблицы и этого поля там ещё нет. Будем так же считать, что мы не знаем как и где точно расположена таблица «ТЗ2» в исходном запросе – в какой выборке и является ли она источником или соединением (но, всё-таки, для простоты – будем считать, что общая структура вложенности запросов остаётся как предыдущем примере)
Сз = Новый СхемаЗапроса();
	Сз.УстановитьТекстЗапроса(Текст); //Предыдущий текст
	ВерхнийЗапрос = Сз.ПакетЗапросов[0];
	ВложенныйЗапрос = ВерхнийЗапрос.Операторы[0].Источники[0].Источник.Запрос;
	Операторы = ВложенныйЗапрос.Операторы;
	//Считаем, что мы не знаем в каком из объединяемых запросов находится нужная нам таблица "ТЗ2"
	//ПозицияПоля = 0; //При объединении нужно праивльно соблюдать порядок полей - будем вставлять в начало (если получится)
	ПозицияПоля = -1; //Пока нет позиции
	Если ВложенныйЗапрос.Колонки.Найти("Аналитика")=неопределено Тогда 
		//Только если нужного поля ещё нет
		Для каждого Оператор из Операторы Цикл
			Источник = Оператор.Источники.НайтиПоИмени("ТЗ2"); //Спасибо хоть за какую-то возможность поиска - ранее её не было
			//Если Источник=неопределено Тогда //Приходилось перебрать вручную
			//	Для каждого СтрокаИсточник из Оператор.Источники Цикл 
			//		Если ТипЗнч(СтрокаИсточник.Источник)=Тип("ОписаниеВременнойТаблицыСхемыЗапроса") 
			//		     И СтрокаИсточник.Источник.ИмяТаблицы = "ТЗ2"
			//		Тогда
			//			Источник = СтрокаИсточник.Источник;
			//			прервать;
			//		КонецЕсли;
			//	КонецЦикла
			//КонецЕсли;			
			Если Источник=неопределено Тогда //В этой выборке нет ТЗ2
				//Оператор.ВыбираемыеПоля.Добавить("НЕОПРЕДЕЛЕНО",ПозицияПоля);
				//Вставить в начало не просто - предыдущая строка перетирает поле "А"
				Позиция = Оператор.ВыбираемыеПоля.Количество(); //Это обязательно
				НовоеПоле = Оператор.ВыбираемыеПоля.Добавить("НЕОПРЕДЕЛЕНО", Позиция);
				Если ПозицияПоля=-1 Тогда 
					ПозицияПоля = Оператор.ВыбираемыеПоля.Индекс(НовоеПоле); //какое-то извращение
				КонецЕсли;
			Иначе
				ИмяТаблицы = "РегистрСведений.ЗначенияСвойствОбъектов";
				ПсевдонимТаблицы = "ЗначенияСвойствОбъектовСвойство1";
				//Упрощённая модель поиска (операется на фиксированный псевдоним;
				//иначе был бы дикий перебор с анализом необъектного условия соединения)
				Если Операторы[0].Источники.НайтиПоПсевдониму(ПсевдонимТаблицы) = неопределено Тогда
					Если Источник.Источник.ДоступныеПоля.Найти("Б")=неопределено Тогда 
						//У внешней временной таблицы это обычная ситуация
						Источник.Источник.ДоступныеПоля.Добавить("Б");
					КонецЕсли;
					//или через "одно место": ВложенныйЗапрос.ДоступныеТаблицы[4].Состав[0]
					//поиск там тоже есть, но такой же - по имени
					Ист = Оператор.Источники.Добавить(ИмяТаблицы,ПсевдонимТаблицы); 
					УсловиеСоединения=ПсевдонимТаблицы+".Свойство = &Свойство1 
										    |И "+ПсевдонимТаблицы+".Объект = ТЗ.Б"; 
					//Условия соединений до сих пор не имеют объектной модели API
					Источник.Соединения.Добавить(Ист,УсловиеСоединения);
				//Иначе - таблица есть, но поля для колонки "Аналитика" всё-равно нет - будем добавлять
				КонецЕсли;
				//Оператор.ВыбираемыеПоля.Добавить("Свойство1.Значение",ПозицияПоля);
				Позиция = Оператор.ВыбираемыеПоля.Количество(); //Это обязательно
				НовоеПоле  = Оператор.ВыбираемыеПоля.Добавить(ПсевдонимТаблицы+".Значение", Позиция);
				Если ПозицияПоля=-1 Тогда
					ПозицияПоля = Оператор.ВыбираемыеПоля.Индекс(НовоеПоле); //какое-то извращение
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Если ПозицияПоля>=0 Тогда
			//Вот, как-то так надо размещать поле в заданной позиции (0 - начало)
			ВложенныйЗапрос.Колонки.Переместить(ПозицияПоля,0); 
			//А вот так придёся задать псевдоним новому полю
			ВложенныйЗапрос.Колонки[0].Псевдоним = "Аналитика"; 
		КонецЕсли;
	КонецЕсли;
	Если ВерхнийЗапрос.Колонки.Найти("Аналитика")=неопределено Тогда
		//Удивительно - группировка добавится автоматически
		НовПоле = ВерхнийЗапрос.Операторы[0].ВыбираемыеПоля.Добавить("Аналитика"); 
		Позиция = ВерхнийЗапрос.Операторы[0].ВыбираемыеПоля.Индекс(НовПоле);
		ВерхнийЗапрос.Колонки.Переместить(Позиция,0);
	КонецЕсли;

	Текст = Сз.ПолучитьТекстЗапроса();
Показать


Вроде бы очень простая задача – а сколько запутанного алгоритма пришлось наваять – новичок тут утонет (хе – а это хорошая задача для теста на собеседовании – небось я там сам вверху ошибок поналяпал – ну по-придираться там точно много де можно). И это всего одна таблица и одно поле – а если нужно было бы их несколько вставлять, а условия интеграции были бы чуть посложнее тривиальной привязки к заданным именам и псевдонимам?
Всё-таки алгоритм изменения запроса с целью добавления новой таблицы и поля должен быть проще и понятнее – вот как-то так
КСЗ = Новый КонструкторСхемыЗапроса(Текст);
	ВерхнийЗапрос = КСЗ.Пакеты[0];
	ВложенныйЗапрос = ВерхнийЗапрос.НайтиТаблицуПоПсевдониму("Вложенный"); //Считаем, что он всегда есть
	//таблица может быть любой, как внутри секци "ИЗ", так и внутри секции "СОЕДИНЕНИЕ"
	//Поиск выше делается последовательно по всем операторам-выборки пакета
	//Иначе нужно коретизировать оператор КСЗ_2.Пакеты[0].Оператор[0].Таблицы.НайтиПоПсевдониму
	КолонкаАналитика = ВложенныйЗапрос.Колонки.НайтиПоПсведониму("Аналитика");
	Если КолонкаАналитика=неопределено 
		//Создадим описание источника (вот так, коли в 1С нет статических методов встроенных классов)
		ИсточникТ32 = КСЗ.СоздатьОписаниеИсточника("ТЗ2",Тип("ВременнаяТаблицаСхемыЗапроса")); 
		Иерархически = Истина;
		ТаблицаТ32 = ВложенныйЗапрос.НайтиТаблицуПоИсточнику(ИсточникТ32,Иерархически);
		//Поиск выше делается последовательно по всем операторам-выборки запроса, 
		//с заходов во все подзапрсы (вложенные и соединения)
		//Ещё можно было бы найти сразу все МассивТ32 = ВложенныйЗапрос.НайтиТаблицы(ПараметрыОтбора,Иерархически)
		Если ТаблицаТ32 <> неопределено Тогда
			ИсточникСвойство1 = КСЗ.СоздатьОписаниеИсточника("РегистрСведений.ЗначенияСвойствОбъектов",
																		Тип("ДоступнаяТаблицаСхемыЗапросов"));
			ТаблицаСвойство1 = КСЗ.СоздатьОписаниеТаблицы(ИсточникСвойство1);
			УсловиеСвойство1 = КСЗ.СоздатьОписаниеУсловия(ТаблицаСвойство1, 
																	"$ТаблицаИсточник",
																	"Свойство",
																	КСЗ.СоздатьОписаниеПараметраЗапроса("Свойство1");
			ТаблицаСвойство1.УсловияСоединений.Добавить(УсловиеСвойство1);
			УсловиеСвойство1 = КСЗ.СоздатьОписаниеУсловия(ТаблицаСвойство1, 
																	"$ТаблицаИсточник",
																	"Объект",
																	"$ТаблицаПриемник",
																	"Б");
			ТаблицаСвойство1.УсловияСоединений.Добавить(УсловиеСвойство1);
			//Выше хитрый приём - создаём описание таблицы, с заданными условиями на соединение
			//Теперь можно найти таблицу, которая будет не только с данным источником данных
			//Но и иметь определённые условия применения (в данном случае - любого соединения
			//Тем самым мы не операемся на псевдоним, а ищем именно правильное назначение
			//Среди таблиц-соединений к нашей целевой табоицы "ТЗ2
			ТаблицаТ32.Соединения.НайтиТаблицу(ТаблицаСвойство1);
			Если Таблица = неопределено Тогда
				//Не нашли - добавим - у нас уже всё есть				
				ТаблицаСвойство1 = ТаблицаТ32.Соединения.ДобавитьЛевоеСоединение(ТаблицаСвойство1); 
				//Соединение - это тоже Таблица
			Иначе
				ТаблицаСвойство1 = Таблица; //Используем реально найденную таблицу
			КонецЕсли;
			КолонкаЗначениеИсточник = ТаблицаТ32.ДопустимыеКолонки.НайтиПоПсевдониму("Значение");
			КолонкаЗначение = КСЗ.СоздатьОписаниеКолонкиТаблицы(КолонкаЗначениеИсточник);
			КолонкаЗначение.Псевдоним = "Аналитика";
			//Для однозначной трактовки переданных значений
			ЗначениеНеопределено = КСЗ.СоздатьОписаниеВыражения("НЕОПРЕДЕЛЕНО"); 
			ЗначениеПоумолчанию = ЗначениеНеопределено;
			//Тут Владелец - это запрос - владелец данной таблицы (а не Оператор этого запроса)
			Колонка = ТаблицаТ32.Владелец.Колонки.Найти(КолонкаЗначение.Псевдоним);
			Если Колонка = неопределено Тогда
				//Колонки нет
				Позиция = 0; //Самая первая колонка
				//Добавим новую колонку в запрос владелец (источники все внутри КолонкаЗначение)
				Колонка = ТаблицаТ32.Владелец.Колонки.Вставить(Позиция, КолонкаЗначение, ЗначениеПоумолчанию);
				//ЗначениеПоумолчанию - позволяет сразу указать значение для других операторов выборки запроса
				//Если нужно было бы вставить для разных операторов объединения специфические значения поля
				//то их нужно было бы заполнить в специальной коллекции типа "КомплексноеОписаниеКолонки"
				//И передавть в один из методов добавления/обнволения колонки
				//Ну или обрабатывать Колонку по её полям для каждой таблицы - заменяя нужные поля методом "Обновить
			Иначе
				//Метод Обновить позволяет обновить поля  существующей комплексной колонки - для заданной таблицы
				//Не изменяя поля дргух таблиц (других операторов объединения)
				Колонка = ТаблицаТ32.Владелец.Колонки.Обновить(Колонка, КолонкаЗначение
			КонецЕсли
			
			//Осталось самое хитрое - протолкнуть созданную колонку "Аналитика" наверх
			//Например во ВложенныйЗапрос
			Если ТаблицаТ32.Владелец <> ВложенныйЗапрос Тогда
				Позиция = 0; //Самая первая колонка
				Пробросить = КС3.Перечисления.РежимПроброскиКолонки.ПроброситьВверх; //А ещё можно вниз
				КолонкаАналитика = ВложенныйЗапрос.Колонки.Вставить(Позиция, Колонка,,,Пробросить); 
				//Вот таквот просто - Колонка - определена где-то воложенной таблице
				//Но если добавлять её во родительскую таблицу - то она будет автоматически проброшена вверх
				//От своего источника до приёмника (если это, возможно, конечно) - иначе - вернёт 
			Иначе
				КолонкаАналитика = Колонка;
			КонецЕсли;
		КонецЕсли;
		
	КонецЕсли;
	
	Если ВерхнийЗапрос.Колонки.НайтиПоПсевдониму("Аналитика")=неопределено 
		//Можно было бы пробросить сразу сюда - но для разнообразия добавим вручную
		Позиция = 0
		//Можно было бы так
		//Пробросить = КС3.Перечисления.РежимПроброскиКолонки.ПроброситьВверх; //А ещё можно вниз
		//ВерхнийЗапрос.Вставить(Позиция, НоваяКолонка,,,Пробросить);
		//Но сделаю по-другому - просто найдту доступную колонку во вложенном запросе 
		//(если он сразу внутри верхнего запроса) - это просто демонстрация альтернативного подхода
		КолонкаАналитикаИсточник = ВерхнийЗапрос.ДопустимыеКолонки.НайтиПоПсевдониму("Аналитика");
		КолонкаАналитика = КСЗ.СоздатьОписаниеКолонкиТаблицы(КолонкаЗначениеИсточник);
		ТаблицаТ32.Владелец.Колонки.Вставить(Позиция, КолонкаАналитика
	КонецЕсли;
Показать


Может получилось так же громоздко (даже если убрать все комментарии) – зато, на мой взгляд, куда нагляднее и мощнее типового подхода.
А ещё все эти таблицы можно было бы откреплять от одной схемы и легко добавлять в другие схемы, без изменений или с изменениями – увеличивая повторяемость кода
ДругойКСЗ = Новый КонструкторСхемыЗапроса();
ДругойКСЗ.Пакеты[0].Таблицы.Добавить(ТаблицаТ32.Скопировать(ДругойКСЗ));


А если бы ещё в запросах можно было бы прикреплять точки интеграции (и не тупые управляющие комментарии как в типовых конфигурациюя) – чтобы при анализе схемы запроса можно было бы на них оператся – вообщен круто было бы
Текст  = "
	         |ВЫБРАТЬ
	         |	СУММА(Вложенный.А) КАК А
	         |ИЗ
	         |	(ВЫБРАТЬ
	         |		ТЗ.А КАК А
	         |	ИЗ
	         |		ТЗ КАК ТЗ ИНТЕГРАЦИЯ(Точка1)
	         |	
	         |	ОБЪЕДИНИТЬ ВСЕ
	         |	
	         |	ВЫБРАТЬ
	         |		ТЗ.А
	         |	ИЗ
	         |		ТЗ2 КАК ТЗ ИНТЕГРАЦИЯ(Точка2)) КАК Вложенный";
КСЗ = Новый КонструкторСхемыЗапроса(Текст);
Точка2 = КСЗ.НайтиТочкуИнтеграции(Точка2);
Если ТипЗнч(Точка2.Владелец)=Тип("ТаблицаСхемыЗапроса") И Точка2.Псевдоним = "ТЗ2") Тогда
	ТаблицаТЗ2 = Точка2.Владелец;
	//И далее алгоритм обработки
КонецЕсли;
Показать


Эх, мечты мечты….
Но нештатную библиотеку «РаботаСоСхемойЗапроса» и «МодельЗапроса» изучить конечно будет любопытно – хотя как-то всё тут не привычно (хотя и напоминает мной горячо любимый LINQ – как-то всё сложновато (возможно и моё, вышеприведённое, предложение люди тоже назовут сложным) – наверное, это просто в статье очень сложно и ненаглядно преподносится материал (хотя визуально статья оформлена очень хорошо)

P.S.
За ошибки в тексте в коде алгоритимов прошу прощения
jan-pechka; comptr; Yashazz; +3 Ответить
16. kalyaka 1105 06.03.21 11:51 Сейчас в теме
(14) 1. глубокая модификации пакета запросов ненадежна. В случае работы с текстовыми шаблонами по хорошему нужен парсер, а это уже практически схема запроса. При работе со схемой велика вероятность, что изменения в одном запросе пакета затронут все остальные. Даже если работать аккуратно, то в процессе модификации могут возникать промежуточные состояния схемы, после которых схема может не вернуться в точности в первоначальное состояние. Например могут поменяться псевдонимы полей, может исчезнуть соединение и не восстановиться и т.д.
Поэтому я для себя сделал вывод: можно модифицировать только путем достраивания последнего запроса пакета: добавление связей, полей, условий. Можно также безопасно копировать оператор выбрать для достраивания запроса путем добавления объединений. Для такого рода модификаций у меня как раз есть функции (пока только в библиотеке): КопияОператора, ЗаменитьИсточник, УдалитьПоле, ВыражениеПоля и т.д.

2. согласен. По поводу "нельзя переносить готовые источники из других схем", то это вопрос необходимости, можно и добавить :)

3. это проблема промежуточных состояний схемы. Схема всегда должна быть валидна.

4. не всегда, например в следующей подсистеме происходит обволакивание любых условий в группу И для любых запросов. Т.е. Устойчивый алгоритм написать можно именно как раз за счет использования схемы.

По примеру 1 приведу иллюстрацию в модели запроса. Я предпочитаю использовать временные таблицы вместо вложений (с вложениями тоже можно):
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("ПолеА",  ОбщегоНазначения.ОписаниеТипаСтрока(10));
Таблица.Колонки.Добавить("ПолеБ",  ОбщегоНазначения.ОписаниеТипаЧисло(15, 2));

МодельЗапроса = Общий.МодельЗапроса();
МодельЗапроса.ЗапросПакета().Поместить("ВТ_1").Выбрать().Источник("&Таблица", "Таблица", Таблица).Поле("*");
МодельЗапроса.ЗапросПакета().Поместить("ВТ_2").Выбрать().Источник("&Таблица", "Таблица", Таблица).Поле("*");
МодельЗапроса.ЗапросПакета().Поместить("ВТ_12")
	.Выбрать()
		.Источник("ВТ_1")
		.Поле("ПолеА")
		.Поле("ПолеБ", "СуммаПоПолюБ_1")
		.Поле("0", "СуммаПоПолюБ_2")
	.ОбъединитьВсе()
		.Источник("ВТ_2")
		.Поле("ПолеА")
		.Поле("0", "СуммаПоПолюБ_1")
		.Поле("ПолеБ", "СуммаПоПолюБ_2");
МодельЗапроса.ЗапросПакета().Источник("ВТ_12")
	.Сумма("СуммаПоПолюБ_1")
	.Сумма("СуммаПоПолюБ_2")
	.Поле("*");
Показать
, что эквивалентно тексту запроса:
ВЫБРАТЬ
	Таблица.ПолеА КАК ПолеА,
	Таблица.ПолеБ КАК ПолеБ
ПОМЕСТИТЬ ВТ_1
ИЗ
	&Таблица КАК Таблица
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Таблица.ПолеА КАК ПолеА,
	Таблица.ПолеБ КАК ПолеБ
ПОМЕСТИТЬ ВТ_2
ИЗ
	&Таблица КАК Таблица
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВТ_1.ПолеА КАК ПолеА,
	ВТ_1.ПолеБ КАК СуммаПоПолюБ_1,
	0 КАК СуммаПоПолюБ_2
ПОМЕСТИТЬ ВТ_12
ИЗ
	ВТ_1 КАК ВТ_1

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	ВТ_2.ПолеА,
	0,
	ВТ_2.ПолеБ
ИЗ
	ВТ_2 КАК ВТ_2
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	СУММА(ВТ_12.СуммаПоПолюБ_1) КАК СуммаПоПолюБ_1,
	СУММА(ВТ_12.СуммаПоПолюБ_2) КАК СуммаПоПолюБ_2,
	ВТ_12.ПолеА КАК ПолеА
ИЗ
	ВТ_12 КАК ВТ_12

СГРУППИРОВАТЬ ПО
	ВТ_12.ПолеА
Показать
Теперь модифицируем его. Скажем текст запроса был получен и к нему добавлено условие:
ТекстЗапроса = МодельЗапроса.ПолучитьТекстЗапроса();
МодельЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
МодельЗапроса.Отбор("СУММА(ВТ_12.СуммаПоПолюБ_1) = СУММА(ВТ_12.СуммаПоПолюБ_2)");
, на выходе получим:
ВЫБРАТЬ
	СУММА(ВТ_12.СуммаПоПолюБ_1) КАК СуммаПоПолюБ_1,
	СУММА(ВТ_12.СуммаПоПолюБ_2) КАК СуммаПоПолюБ_2,
	ВТ_12.ПолеА КАК ПолеА
ИЗ
	ВТ_12 КАК ВТ_12

СГРУППИРОВАТЬ ПО
	ВТ_12.ПолеА

ИМЕЮЩИЕ
	СУММА(ВТ_12.СуммаПоПолюБ_1) = СУММА(ВТ_12.СуммаПоПолюБ_2)
Показать
19. Yashazz 4790 08.03.21 18:08 Сейчас в теме
(16) Схема всегда должна быть валидна? Чего это вдруг? Схема должна быть валидна в момент применения, а до того хоть полуоборванная и кривая.
Darklight; +1 Ответить
22. kalyaka 1105 09.03.21 11:22 Сейчас в теме
(19) я исхожу их требования для объекта СхемаЗапроса в платформе.
20. Darklight 33 09.03.21 10:06 Сейчас в теме
(16)
1.
глубокая модификации пакета запросов ненадежна

Всё определяется лишь возможностями API движка. Я, вот, привёл примеры того, как легко делать вполне надёжную глубокую модификацию. И эти примеры я построил на коленке за час. А если подумать над этой задачей подольше - можно и что-то более изящное придумать.
Но....
Во-первых, для глубокой модификации лучше работать через точки интеграции, которые заранее заданы в запросе (пример приводил)
Во-вторых, запросы вообще лучше не модифицировать. Тут в корне нужна иная модель их построения - более декларативная (намекну на типовой "ПостроительЗапроса", но, всё-таки, я имею в виду ещё более продвинутый API, с несколько иным подходом к использованию). Когда запрос изначально собирается по частям из абстрактных частей - которые обретаю целостность и текстовое представление только в момент выполнения. Это особенно актуально на фоне расширений конфигурации 1С - когда модификации вносятся совершенно в других конфигурациях (расширениях) в других блоках программного кода. Но это кардинальные изменения вообще в принципы построения запросов - и это не приемлемо для запросов, которые изначально не применят такую методику.
В-третьих. Моё мнение такое - что для повышения надёжности и производительности - большая часть модификации должна осуществляться статически при компиляции - т.е. развиваться должно макропрограммирование и, опять-же, декларативное программирование, когда - все команды (которые статически определены) по внесению изменений (первичному построению) запроса выполняются ещё при конфигурировании (компиляции) - и можно сразу проконтролировать итоговый результат (а в идеале ещё и выполнить юнит-тесты). Да даже модификация с ветвлением, по возможности, должна быть статической - просто можно сразу сгенерировать несколько версий запроса (и тоже прогнать их по юниттестам), и динамически только выбирать нужный готовый запррос. Да, даже с абстрактными/неполными (на стадии компиляции) полями - при желании, можно было бы тоже готовить условно статические тексты запросов и контролировать их общий синтаксис и частично выполнение (где в рантайме просто делать простую автоподстановку нужных полей - это ещё ПостроительЗапроса умел делать).

Но - такие радикальные изменения, сложны в реализации, особенно без поддержки со стороны платформы, и особенно со стороны типовых конфигураций. В 1С Предприятие 8.x вряд ли стоит ждать таких новаций :-(

В случае работы с текстовыми шаблонами по хорошему нужен парсер, а это уже практически схема запроса

Конечно нужен - нужна полноценная объектная модель всего запроса. "СхемаЗапроса" её даёт, но её API ужасен, и да - он теряет комментарии. К счастью, язык запросов не такой уж сложный - чтобы сделать к нему свой парсер

3. Вот не считаю, что схема всегда должна быть валидна. Её валидность можно было бы проверять только в момент финальной сборки (или отдельным методом). Всё-таки, когда со схемой идёт работа через объектную модель, очень сложно внести в неё совсем уж несовместимые изменения – их, возможно, и стоит пресекать сразу. В остальном – запрос в схеме – это не более чем вложенный набор коллекций простых элементов – и его можно модифицировать как угодно – пока дело не дойдёт до финальной сборки и кодогенерации.
4. Ну, во-первых, я всё-таки говорил про типовой объект «СхемаЗапроса»
А во-вторых…

ТекстЗапроса = МодельЗапроса.ПолучитьТекстЗапроса();
МодельЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
МодельЗапроса.Отбор("СУММА(ВТ_12.СуммаПоПолюБ_1) = СУММА(ВТ_12.СуммаПоПолюБ_2)");

Честно, не могу понять – как тут определяется правильное место в пакете запросов.
Что здесь «ВТ_12»? Это временная таблица или псевдоним таблицы данных? Или поле? Ведь внутри запроса и то и другое имя может встречаться не один раз раз.
В-третьих, добавлять что-то новое – куда проще, чем вносить изменения в существующее.
Да , и в приведённом примере внести такую модификацию текстом – так же просто (и так же не надёжно при изменении структуры исходного запроса) – не показано ни одного преимущества!

А вот это я вообще не могу понять зачем?

ТекстЗапроса = МодельЗапроса.ПолучитьТекстЗапроса();
МодельЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
23. kalyaka 1105 09.03.21 11:33 Сейчас в теме
(20)
не могу понять – как тут определяется правильное место в пакете запросов
Место определяется последним запросом пакета и последним оператором выбрать.

В этом примере я хотел продемонстрировать как можно начать схему в одном месте и затем продолжить в другом. При этом функции ПолучитьТекстЗапроса() и УстановитьТекстЗапроса() по сути выполняют роль сериализации в текст и восстановления из текста для объекта схемы запроса.
в приведённом примере внести такую модификацию текстом – так же просто
Однако при работе с текстом необходимо определить наличие раздела ИМЕЮЩИЕ. Хотя конечно, можно использовать продвинутую библиотеку, которая это сделает и добавить условие будет также просто, как и в представленном примере. Но последнее означает, что просто добавить условие удобнее, чем перед этим анализировать текст.
24. Darklight 33 09.03.21 13:35 Сейчас в теме
(23)Продолжить в другом месте - вообще никогда не было проблемой. Вот внести изменения где-то внутри - вот это всегда проблема!
Поэтому в (14) я и привёл такие банальные и популярные задачи такой модификации - которые не очень сложно решаются без подходящего API (и все нынешние методики внесения таких изменений нестабильны, при изменении структуры исходного текста запроса). Поэтому там же я привёл своё виденье API для более простого, понятного и стабильного внесения таких изменений не в конец!

Последний абзац вообще не понял
25. kalyaka 1105 09.03.21 14:04 Сейчас в теме
(24)Ок, осталось дождаться появления таких удобных АПИ. Или, возможно, механизм представлений из ЗУП вполне себе удобный механизм и его можно взять в качестве образца в своих разработках.
Последний абзац вообще не понял
это я про то, что есть выбор: либо работать с текстом, либо вызвать метод.
17. Yashazz 4790 08.03.21 18:04 Сейчас в теме
(14) Целиком поддерживаю каждый пункт, просто мне категорически не под силу сейчас накатать вдумчивый анализ и критику, подобные этой. Со всем этим сталкивался, после чего желание пользоваться схемой запроса пропало напрочь. Сугубо декларативный инструмент смешали с обрабатываемой базой, данными, их структурой и типами. АПИ СКД действительно в разы более проработанная, гибкая, терпимая (вплоть до наплевательского отношения к метаданным - известные "мёртвые" поля с красными крестами не мешают компоновать декларацию). Так что спасибо за детальный разбор. Надеюсь, теперь яснее, почему мне польза сего сомнительна.
13. JohnyDeath 302 03.03.21 09:11 Сейчас в теме
(11) Я как раз типовые запросы подправляю именно схемой, чтобы не вкарячивать в середину огромного запроса свои доп поля и условия. Соответственно обновление на новый релиз проходит намного быстрей
Bassgood; +1 Ответить
15. Darklight 33 04.03.21 17:45 Сейчас в теме
(13)Несколько раз так пробовал - очень тяжело (но тогда ещё сам объект "СхемаЗапроса" был менее развит по API - и работа с ним было очень мучительно).

Пока предпочитаю встраиваться в тексты типовых запросов через СтрЗаменить - т.е. ищу подходящий кусочек кода в запросе, и этой функцией после текста запроса его заменяю на него же + мои добавки. Ещё мощная функция "СтрНайти" - для обработки текста запроса частями (позволяет разбивать его на блоки). Всё бы ничего только два момента:
1. Бывают проблемы с группировками, запросами объединениями и условиями - потому что в них нет псевдонимов.
2. Бывает проблемы с табуляцией - к сожалению нет поиска вхождения символов строки без учета табуляции - а порой бывает нужно обрабатывать именно такие участки - где присутствует шатающаяся табуляция

А со схемой запроса в такого рода задачах - это тоже - правильно спозиционироваться в место - где нужно внести доработки - нормальных средств поиска нет.
И когда запросы слишком комплексные - то обычно и однотипные исправления требуются сразу в нескольких местах - и всех их находить - ну очень неудобно. Чаще всего - банально обработать текст - гораздо проще. И обычно такая текстовая обработка хорошо переживает дальнейшее "развитие запроса" (его последующие типовые и не типовые модификации, которые меняют его структуру)
sml; Yashazz; +2 Ответить
18. Yashazz 4790 08.03.21 18:07 Сейчас в теме
(15) Пробуй потоковым чтением, там поиск и разбивка на фрагменты удобнее; ну и регулярные выражения, чтоб ловить всякую табуляцию и иную ересь. И да, опять же целиком согласен.
21. Darklight 33 09.03.21 10:14 Сейчас в теме
(18)Можете привести пример с потоковым чтением и разбивкой на фрагменты - что-то я пока не соображу что Вы имеете в виду?
Регулярные выражения - мощная штука, жаль не входят в платформу 1С Предприятие, и порой - сами регулярные выражения становятся достаточно сложными при использовании и дальнейшем сопровождении (опять же - в силу отсутствия со стороны платформы поддержки конструкторов, анализаторов, отладчиков). Особенно это всё усложняется при обновлении конфигурации - когда надо понять - нужно ли вносить изменения (и какие) в алгоритм модификации - когда видишь, что типовой запрос изменился (и не видишь нюансы изменения именно в табуляции)
Оставьте свое сообщение