Процессор схемы запроса

19.02.26

Разработка - Инструментарий разработчика

Процессор схемы запроса - инструмент для структурной обработки текстов запросов и работы с виртуальными источниками данных.

Файлы

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

Наименование Скачано Купить файл
Процессор схемы запроса:
.cf 81,73Kb ver:1.0.1
2 6 200 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Приходилось ли вам сталкиваться с подобными задачами?

  • Есть текст запроса, в который нужно добавлять определенные элементы (отборы, группировки, сортировки, итоги и т.п.), в зависимости от некоторых условий.
  • Есть тексты двух или более запросов, которые нужно как-то скомбинировать: соединить или объединить.
  • Нужно динамически сгенерировать текст запроса в соответствии с некоторым заданным описанием.

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

В платформе 1С существует объект СхемаЗапроса, который предоставляет объектную модель запроса и, в общем-то, позволяет решать большинство подобных задач. Но и у него есть свои недостатки.

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

Во-2х, схема запроса не умеет строить объектную модель вложенных запросов выражений (запрос в операторе "В (ВЫБРАТЬ …)").

Ну и в-3х, схема запроса имеет некоторое количество довольно неприятных багов и "особенностей". Пожалуй, самая известная из таких особенностей — это "шибко умное" автоматическое добавление соединений при добавлении источников, совершенно ненужное при программном формировании запроса, но при этом неотключаемое. Ниже будет отдельный раздел, посвященный выявленным ошибкам схемы запроса и путям их обхода.

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

Представляю вам обработку ПроцессорСхемыЗапроса. Приготовиться к лонгриду. Поехали.

 

Обзор возможностей

Структурная обработка запроса

Примеры 1-9

Общие принципы структурной обработки

Базовая навигация

Предметная навигация

Конструирование

Чтение и модификация существующих объектов

Общие методы чтения и записи

Виртуальные источники данных

Пример 10

Необходимые условия прямого внедрения

Параметры виртуальных источников. Примеры 11-13

Виртуальные источники как альтернатива динамическому конструированию

Опции процессора схемы запроса

Выявленные ошибки схемы запроса

Несколько слов о производительности

Форма поставки и требования к платформе

 

Обзор возможностей

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

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

Отсюда, собственно, и получился набор функций процессора, состоящий из двух тематических блоков:

  1. Структурная обработка запроса.
  2. Виртуальные источники данных.

А теперь обо всем по порядку.

 

Структурная обработка запроса

Начну с примеров.

 
 ДИСКЛЕЙМЕР

 

Пример 1

Имеем текст запроса:

 
 Исходный запрос
	Текст1 = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер,
	|	ТранспортныеЕдиницы.КлассТранспорта КАК КлассТранспорта
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы";

 

И хотим добавить в него отбор, сортировку и выбрать "разрешенные первые 50". Изображу желаемый результат вот так:

 
 Желаемый запрос
	Текст1 = 
	"ВЫБРАТЬ
//	|	РАЗРЕШЕННЫЕ ПЕРВЫЕ 50
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер,
	|	ТранспортныеЕдиницы.КлассТранспорта КАК КлассТранспорта
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
//	|ГДЕ
//	|	ТранспортныеЕдиницы.КлассТранспорта = &КлассТранспорта
//	|
//	|УПОРЯДОЧИТЬ ПО
//	|	Номер
	|";

 

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

Если использовать непосредственно схему запроса, то потребуется написать вот такой код:

 
 Код с использованием схемы запроса
	Схема = Новый СхемаЗапроса;
	Схема.УстановитьТекстЗапроса(Текст1);
	Запрос = Схема.ПакетЗапросов[0];
	Запрос.ВыбиратьРазрешенные = Истина;
	Оператор = Запрос.Операторы[0];
	Оператор.КоличествоПолучаемыхЗаписей = 50;
	Оператор.Отбор.Добавить("КлассТранспорта = &КлассТранспорта");
	Запрос.Порядок.Добавить("Номер");
	Текст2 = Схема.ПолучитьТекстЗапроса();

 

А вот так это выглядит с применением процессора:

 
 Код с использованием процессора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Разрешенные().Первые(50)
		.Где("КлассТранспорта = &КлассТранспорта")
		.УпорядочитьПо("Номер")
		.ПолучитьТекстЗапроса();

 

Что мы видим на этом тривиальном примере:

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

Пример 2

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

 
 Желаемый запрос
	Текст1 = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер,
	|	ТранспортныеЕдиницы.КлассТранспорта КАК КлассТранспорта,
	|	ТранспортныеЕдиницы.Грузоподъемность КАК Грузоподъемность
//	|	Автомобили.Марка КАК Марка,
//	|	Автомобили.ТипКузова КАК ТипКузова
//	|ПОМЕСТИТЬ Автомобили
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
//	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Автомобили КАК Автомобили
//	|		ПО ТранспортныеЕдиницы.Ссылка = Автомобили.ТранспортнаяЕдиница
	|ГДЕ
	|	ТранспортныеЕдиницы.КлассТранспорта = &КлассТранспорта";

 

Вот так это делается при помощи процессора:

 
 Код с использованием процессора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Таблица("ТранспортныеЕдиницы")
			.ВнутреннееСоединение().ИзТаблицы("Справочник.Автомобили", "Автомобили")
			.ПоУсловию("ТранспортныеЕдиницы.Ссылка = Автомобили.ТранспортнаяЕдиница")
		.Поля("Марка,ТипКузова")
		.Поместить("Автомобили")
		.ПолучитьТекстЗапроса();

 

А вот как выглядел бы код с использованием схемы запроса:

 
 Код с использованием схемы запроса
	Схема = Новый СхемаЗапроса;
	Схема.УстановитьТекстЗапроса(Текст1);
	Запрос = Схема.ПакетЗапросов[0];
	Оператор = Запрос.Операторы[0];
	Оператор.Источники.Добавить("Справочник.Автомобили", "Автомобили");
	Источник = Оператор.Источники.НайтиПоПсевдониму("ТранспортныеЕдиницы");
	Оператор.Источники[1].Соединения.Очистить(); // спасибо "интеллектуальному" автодобавлению соединений
	Источник.Соединения.Добавить("Автомобили", "ТранспортныеЕдиницы.Ссылка = Автомобили.ТранспортнаяЕдиница");
	Соединение = Источник.Соединения[Источник.Соединения.Количество() - 1];
	Соединение.ТипСоединения = ТипСоединенияСхемыЗапроса.Внутреннее;
	Оператор.ВыбираемыеПоля.Добавить("Марка");
	Оператор.ВыбираемыеПоля.Добавить("ТипКузова");
	Запрос.ТаблицаДляПомещения = "Автомобили";
	Текст2 = Схема.ПолучитьТекстЗапроса();

 

Здесь, очевидно, процессор вне конкуренции.

Пример 3

Добавляем объединение в запрос.

 
 Желаемый запрос
	Текст1 = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер,
	|	ТранспортныеЕдиницы.КлассТранспорта КАК КлассТранспорта
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
	|ГДЕ
	|	ТранспортныеЕдиницы.КлассТранспорта = ЗНАЧЕНИЕ(Перечисление.КлассыТранспорта.Автомобильный)
	|
//	|ОБЪЕДИНИТЬ ВСЕ
//	|
//	|ВЫБРАТЬ
//	|	ТранспортныеЕдиницы.Ссылка,
//	|	ТранспортныеЕдиницы.Наименование,
//	|	ТранспортныеЕдиницы.КлассТранспорта
//	|ИЗ
//	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
//	|ГДЕ
//	|	ТранспортныеЕдиницы.КлассТранспорта = ЗНАЧЕНИЕ(Перечисление.КлассыТранспорта.Железнодорожный)
	|";

 

С процессором это выглядит так:

 
 Код с использованием процессора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Объединить().Все()
		.ИзТаблицы("Справочник.ТранспортныеЕдиницы", "ТранспортныеЕдиницы")
			.Поле("ТранспортныеЕдиницы.Ссылка", "Ссылка")
			.Поле("ТранспортныеЕдиницы.Наименование", "Номер")
			.Поле("ТранспортныеЕдиницы.КлассТранспорта", "КлассТранспорта")
		.Где("ТранспортныеЕдиницы.КлассТранспорта = ЗНАЧЕНИЕ(Перечисление.КлассыТранспорта.Железнодорожный)")
		.ПолучитьТекстЗапроса();

 

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

 
 Вариант с копированием оператора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Оператор(0).Скопировать()
		.Отбор().Элемент(0)
			.УстановитьТекст("КлассТранспорта = ЗНАЧЕНИЕ(Перечисление.КлассыТранспорта.Железнодорожный)")
		.ПолучитьТекстЗапроса();

 

Пример 4

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

 
 Исходный запрос
	Текст1 = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер,
	|	ТранспортныеЕдиницы.КлассТранспорта КАК КлассТранспорта,
	|	ТранспортныеЕдиницы.Грузоподъемность КАК Грузоподъемность
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы";

 

А получить мы хотим вот такой текст:

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

 

Используем процессор:

 
 Код с использованием процессора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Поле(, "Грузоподъемность").Агрегировать("СРЕДНЕЕ")
		.Поле(, "Ссылка").Удалить()
		.Поле(, "Номер").Удалить()
		//.СгруппироватьПо("ТранспортныеЕдиницы.КлассТранспорта") // это необязательно
		.ПолучитьТекстЗапроса();

 

Здесь мы встречаемся с одним из немногих "узкоспециальных" методов процессора. Метод Агрегировать() предназначен специально для "оборачивания" выражения в агрегатную функцию.

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

Пример 5

Сделаем, для разнообразия, что-нибудь действительно полезное, а заодно посмотрим на генерацию запроса с нуля. Например, как будет выглядеть несколько упрощенный аналог функции БСП ОбщегоНазначения.ЗначениеРеквизитаОбъекта с использованием процессора?

 
 Функция ЗначениеРеквизитаОбъекта
Функция ЗначениеРеквизитаОбъекта(Знач Ссылка, Знач ИмяРеквизита)
	
	Возврат Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьПараметр("Ссылка", Ссылка)
		.Выбрать().Первые(1)
		.ИзТаблицы(Ссылка.Метаданные().ПолноеИмя(), "Т")
		.Где("Ссылка = &Ссылка")
		.Поле(ИмяРеквизита, "Реквизит")
		.ПолучитьЗапрос().Выполнить().Выгрузить()[0].Реквизит;
	
КонецФункции

 

Здесь мы впервые видим, что процессор схемы запроса может работать не только со схемой запроса как таковой. Он может хранить параметры запроса и возвращать нам сразу объект Запрос.

Спойлер: он еще и не такое умеет.

Пример 6

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

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

Исходный текст запроса:

 
 Исходный запрос
	Текст1 =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Количество КАК Количество,
	|	Продажи.СуммаВал КАК СуммаВал
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи";

 

Необходимо найти все поля числового типа и применить к ним агрегатную функцию СУММА. Группировки схема запроса добавит автоматически.

 
 Код с использованием процессора
	Процессор = Обработки.ПроцессорСхемыЗапроса.Создать().УстановитьТекстЗапроса(Текст1);
	Для Каждого Псевдоним Из Процессор.КолонкиЗапроса().ИменаЭлементов() Цикл
		ТипЗначения = Процессор.Колонка(Псевдоним).ПолучитьСвойство("ТипЗначения"); // ОписаниеТипов
		Если ТипЗначения.СодержитТип(Тип("Число")) Тогда
			Процессор.Поле(, Псевдоним).Агрегировать("СУММА");
		КонецЕсли;
	КонецЦикла;
	Текст2 = Процессор.ПолучитьТекстЗапроса();

 

И в результате получим такой текст запроса:

 
 Результирующий запрос
	Текст2 =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	СУММА(Продажи.Количество) КАК Количество,
	|	СУММА(Продажи.СуммаВал) КАК СуммаВал
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|
	|СГРУППИРОВАТЬ ПО
	|	Продажи.Номенклатура,
	|	Продажи.Валюта";

 

Пример 7

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

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

 
 Код с использованием процессора
	Запрос2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьПараметр("Состав", Объект.Состав.Выгрузить())
		.ВыбратьИзПараметра("Состав")
		.ПолучитьЗапрос();

 

В результате получим вот такой запрос создания временной таблицы:

 
 Запрос создания временной таблицы из параметра
	Запрос2.Текст =
	"ВЫБРАТЬ
	|	ВЫРАЗИТЬ(Состав.НомерСтроки КАК ЧИСЛО) КАК НомерСтроки,
	|	ВЫРАЗИТЬ(Состав.ТранспортнаяЕдиница КАК Справочник.ТранспортныеЕдиницы) КАК ТранспортнаяЕдиница,
	|	ВЫРАЗИТЬ(Состав.ВесГруза КАК ЧИСЛО(10, 0)) КАК ВесГруза,
	|	ВЫРАЗИТЬ(Состав.ОбъемГруза КАК ЧИСЛО(10, 0)) КАК ОбъемГруза,
	|	ВЫРАЗИТЬ(Состав.ДатаЗагрузки КАК ДАТА) КАК ДатаЗагрузки
	|ПОМЕСТИТЬ Состав
	|ИЗ
	|	&Состав КАК Состав";

 

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

При желании типизацию полей можно отключить, в методе ВыбратьИзПараметра есть необязательный параметр, который за это отвечает.

Пример 8

Вложенные запросы выражений - то, что схема запроса не умеет структурировать. Ну, как не умеет. Главное же - найти правильный подход.

Возьмем запрос и добавим к нему условие отбора - глупое, но зато содержащее вложенный запрос.

 
 Желаемый запрос
	Текст1 = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
//	|ГДЕ
//	|	ИСТИНА В (
//	|		ВЫБРАТЬ ПЕРВЫЕ 1
//	|			ИСТИНА КАК Поле1
//	|		ИЗ
//	|			Справочник.Автомобили КАК Автомобили
//	|		ГДЕ
//	|			Автомобили.ТранспортнаяЕдиница = ТранспортныеЕдиницы.Ссылка)
	|";

 

И сделаем это динамически:

 
 Код с использованием процессора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Где("ИСТИНА В (ВЫБРАТЬ NULL)")
			.ВложенныйВыбрать().Первые(1)
			.ИзТаблицы("Справочник.Автомобили", "Автомобили")
			.Поле("ИСТИНА")
			.Где("Автомобили.ТранспортнаяЕдиница = ТранспортныеЕдиницы.Ссылка")
		.ПолучитьТекстЗапроса();

 

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

Обратите внимание на технику создания вложенного запроса "с чистого листа". Изначально в выражении размещена конструкция "В (ВЫБРАТЬ NULL)". Это необходимо, чтобы "обозначить место", где должен находиться вложенный запрос. Далее используется метод ВложенныйВыбрать(), который "проваливается" во вложенный запрос и очищает его содержимое, подготавливая тот самый "чистый лист". Также у метода ВложенныйВыбрать() есть необязательный параметр Индекс - на случай, если в выражении больше одного вложенного запроса на верхнем уровне вложенности.

Пример 9

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

 
 Желаемый запрос
	Текст1 = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.Наименование КАК Номер
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
	|ГДЕ
	|	ИСТИНА В (
	|		ВЫБРАТЬ ПЕРВЫЕ 1
	|			ИСТИНА КАК Поле1
	|		ИЗ
	|			Справочник.Автомобили КАК Автомобили
	|		ГДЕ
	|			Автомобили.ТранспортнаяЕдиница = ТранспортныеЕдиницы.Ссылка
//	|			И НЕ Автомобили.ПометкаУдаления
	|		)";

 

Код будет выглядеть так:

 
 Код с использованием процессора
	Текст2 = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(Текст1)
		.Отбор().Элемент(0)
			.ВложенныйЗапрос()
			.Где("НЕ Автомобили.ПометкаУдаления")
		.ПолучитьТекстЗапроса();

 

На этом пока закончим с примерами и перейдем к общей теории.

 

Общие принципы структурной обработки запроса

Процессор схемы запроса содержит всего 100 методов структурной обработки. Вы уже достали валидол? Отлично, положите на место. Функциональность процессора охватывает всю полноту объектной модели запроса, вплоть до элементов компоновки данных и таких пыльных углов, как "доступные таблицы запроса". Но в 80% задач вы будете использовать 20% функций. К тому же, я постарался обеспечить максимальную лингвистическую преемственность: имена методов будут звучать знакомо, если вы имели дело с языком запросов или с объектной моделью схемы запроса.

Работа всех методов структурной обработки базируется на понятии текущего контекста (или фокуса). Всю объектную модель запроса можно представить в виде дерева, где корневым узлом является сам объект СхемаЗапроса, а ниже расположена иерархия разнообразных объектов и коллекций. Фокус — это указатель на узел дерева, в котором мы в текущий момент находимся. Мы можем перемещать фокус по дереву, а относительно текущего узла дерева совершать некоторые действия - создавать новые узлы-объекты, искать, удалять, двигать объекты в коллекциях, а также читать и изменять свойства объектов. В начальном состоянии фокус позиционируется на самом объекте СхемаЗапроса.

Соответственно, все методы структурной обработки делится на 3 основные группы:

  1. Методы-навигаторы - служат для перемещения фокуса по дереву объектов.
  2. Методы-конструкторы - для создания новых объектов.
  3. Методы чтения/записи - для чтения и модификации содержимого существующих объектов.

Базовая навигация

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

  • Объект(Имя) - позиционируется на вложенном объекте с указанным именем. Например, Объект("Колонки") позиционируется на коллекции колонок текущего запроса - разумеется, при условии, что текущий фокус указывает на объект типа ЗапроВыбораСхемыЗапроса.
  • Родитель() - позиционируется на родительском объекте текущего объекта.
  • Элемент(Индекс) - позиционируется на элементе с указанным индексом в текущей коллекции.
  • ЭлементПоИмени(Имя) - позиционируется на элементе с указанным именем в текущей коллекции. Разумеется, это должна быть коллекция, элементы которой имеют имена или псевдонимы.
  • ЭлементПоПсевдониму(Псевдоним) - позиционируется на элементе с указанным псевдонимом. Служит альтернативой предыдущему методу в тех случаях, когда у элементов есть одновременно имена и псевдонимы (как в коллекции Источники).
  • ЭлементПоВыражению(Выражение) - позиционируется на элементе с указанным текстом выражения. Работает только для элементов, содержащих выражение, таких как выбираемые поля, выражения отбора, группировки и т.п.
  • Предыдущий() - позиционируется на предыдущем соседе элемента в текущей коллекции.
  • Следующий() - позиционируется на следующем соседе элемента в текущей коллекции.

Все методы из этого списка, кроме двух первых, имеют опциональный параметр Обязательный. Если он Истина (а он по умолчанию Истина), то будет вызвано исключение, если элемент коллекции не найден. Если Ложь, то метод просто вернет Неопределено, что негативно скажется на "текучести", но зато пригодится при структурном анализе вариативного запроса.

Предметная навигация

Методов предметной навигации около 40, каждый из них предназначен для позиционирования на объекте или коллекции определенного типа. Их имена почти всегда повторяют (в сокращенном виде) имена объектов схемы запроса. Все они звучат как имена существительные в именительном падеже. В единственном числе - имена объектов, в множественном числе - имена коллекций.

Некоторые методы предметной навигации (не все):

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

И т.д. и т.п. Полное описание методов можно найти в программном интерфейсе обработки ПроцессорСхемыЗапроса, там каждый метод снабжен подробным документирующим комментарием.

Конструирование

Методов-конструкторов тоже почти 40 (магическое число однако). Каждый из них предназначен для создания объекта определенного типа. Некоторые сочетают в себе функции конструктора и навигатора, т.е. могут позиционироваться на существующем объекте. Некоторые могут также изменять существующий объект вместо добавления нового.

Названия методов в основном созвучны ключевым словам языка запросов (в пределах возможного, конечно). Практически все методы-конструкторы позиционируют фокус на том объекте, который создают.

Некоторые методы конструирования (не все):

  • Выбрать() - создает новый запрос выбора в пакете запросов корневой схемы.
  • ВложенныйВыбрать() - позиционируется на вложенном запросе текущего контекста и полностью очищает содержимое запроса. В контексте выражения может иметь необязательный параметр Индекс - для указания одного из нескольких запросов выражения.
  • Объединить() - добавляет новый оператор (объект ОператорВыбратьСхемыЗапроса) в текущем запросе. По умолчанию в запросе всегда есть 0-й оператор, поэтому создание любого дополнительного оператора порождает объединение. Вызов эквивалентен конструкции языка запросов "ОБЪЕДИНИТЬ". Цепочка вызовов Объединить().Все() эквивалентна конструкции "ОБЪЕДИНИТЬ ВСЕ". Обратите внимание, что после этого не нужно вызывать метод Выбрать() - оператор выбора и так уже создан методом Объединить(). Вызов метода Выбрать() приведет к созданию нового объекта ЗапросВыбораСхемыЗапроса в пакете запросов.
  • ИзТаблицы(ИмяТаблицы, Псевдоним) - добавляет источник-таблицу в источники текущего оператора. Процессор автоматически определяет, является ли таблица одной из доступных таблиц или описанием необъявленной временной таблицы, и создает источник соответствующего типа. В итоге позиционируется на созданном объекте типа ТаблицаСхемыЗапроса или ОписаниеВременнойТаблицыСхемыЗапроса.
  • ИзЗапроса(Псевдоним) - добавляет источник-вложенный запрос в источники текущего оператора и позиционируется на созданном объекте ВложенныйЗапросСхемыЗапроса. После этого можно вызвать ВложенныйЗапрос() или ВложенныйВыбрать(), чтобы "провалиться" в сам запрос.
  • ВнутреннееСоединение(Псевдоним) - добавляет внутреннее соединение с указанным источником в соединения текущего источника. Источник с таким псевдонимом уже должен существовать. Есть аналогичные методы ЛевоеСоединение, ПравоеСоединение, ПолноеСоединение.
  • ВнутреннееСоединение() - без параметра создает т.н. "открытое соединение". На самом деле ничего не создает, просто позиционируется на коллекции соединений текущего источника и включает режим ожидания соединения. Следующим вызовом должен быть добавлен новый источник (методом ИзТаблицы или ИзЗапроса). В результате будет автоматически создано соединение с этим источником (если такой вызов не будет сделан в текущем контексте, открытое соединение теряется). Таким способом можно порождать соединения (в т.ч. вложенные соединения) в синтаксическом стиле, подобном языку запросов (см. Пример 2).
  • ПоУсловию(Выражение) - добавляет условие текущего соединения.
  • ПоУсловию(Выражение, Индекс) - изменяет условие существующего соединения с указанным индексом. Дело в том, что если условие соединения состоит из нескольких выражений, соединенных операцией "И", схема запроса при разборе текста запроса автоматически раскладывает такое соединение на несколько объектов СоединениеИсточникаЗапросаСхемыЗапроса - получается по сути группа из нескольких соединений между одной и той же парой источников (именно так мы их видим в конструкторе запроса в конфигураторе). Поэтому и нужен параметр Индекс - он указывает элемент в этой группе соединений. Не всегда удобно работать с такой моделью соединений, поэтому в процессоре предусмотрена опция КонкатенацияУсловийСоединений (см. в соответствующем разделе).
  • Поле(Выражение, Псевдоним) - добавляет поле в выбираемые поля текущего оператора или текущей вложенной таблицы. Если не указать псевдоним, он будет присвоен автоматически схемой запроса. Если указать псевдоним уже существующего поля, то будет установлено выражение существующего поля. Если не указывать выражение, а только псевдоним, то метод работает как навигатор - просто позиционируется на заданном поле.
  • Где(Выражение) - добавляет выражение отбора текущего оператора.
  • СгруппироватьПо(Выражение) - добавляет выражение группировки текущего оператора.
  • УпорядочитьПо(Выражение, Направление) - добавляет выражение порядка текущего запроса.

И т.д. и т.п.

Чтение и модификация существующих объектов

Этих методов не так много, они универсальны.

  • ТипОбъекта() - возвращает тип текущего объекта.
  • ИмяОбъекта() - возвращает имя текущего объекта (как он именуется в родительском объекте).
  • ИндексОбъекта() - возвращает индекс текущего объекта в родительской коллекции.
  • ПолучитьСвойство(Имя) - получить значение свойства текущего объекта.
  • УстановитьСвойство(Имя, Значение) - установить значение свойства текущего объекта.
  • ПолучитьТекст() - возвращает текст текущего объекта - схемы, запроса или выражения.
  • УстановитьТекст(Текст) - устанавливает текст текущего объекта - схемы, запроса или выражения.

Работа с коллекциями:

  • Количество() - возвращает количество элементов текущей коллекции.
  • Удалить() - удалить текущий объект из родительской коллекции и позиционироваться на коллекции.
  • Скопировать() - скопировать текущий объект в родительской коллекции и позиционироваться на созданной копии. Копирование поддерживается только в двух типах коллекций: это пакет запросов и коллекция операторов запроса.
  • Переместить(НовыйИндекс) - переместить текущий объект на указанную позицию в родительской коллекции, фокус сохраняется на текущем объекте.
  • Сдвинуть(Смещение) - сдвинуть текущий объект на указанное число позиций в родительской коллекции, фокус сохраняется на текущем объекте. Перемещение и сдвиг поддерживаются не во всех коллекциях, см. детальные описания методов в программном интерфейсе.
  • Очистить() - очистить текущую коллекцию.
  • ИменаЭлементов() - возвращает массив имен или псевдонимов элементов текущей коллекции. Работает не во всех коллекциях, а только там, где у элементов есть имена или псевдонимы.

Общие методы чтения и записи

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

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

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

 

Виртуальные источники данных

Если вы дочитали до сюда, то не зря. Сейчас на ваших глазах будет совершаться революция в деле использования запросов как таковых. Шутка. Или нет.

Идея виртуальных источников данных родилась в ходе решения задачи разграничения архитектурных уровней.

У запросов, а точнее - у традиционного способа их использования, есть один крохотный недостаток, убивающий на корню любые попытки создать более-менее чистую архитектуру (за определением термина "чистая архитектура" отсылаю к одноименной книге Роберта Мартина). Дело в том, что мы привыкли использовать запросы где угодно - там, где считаем это целесообразным с точки зрения обеспечения высокой производительности при выборке данных из БД. Мы делаем это на любых архитектурных уровнях (неважно, знаем ли мы об их существовании). А запросы при этом лезут на самый нижний архитектурный уровень - непосредственно к структуре БД (ИБ, если быть точным). Тем самым нарушая всякие архитектурные границы и создавая нисходящие зависимости (т.е. зависимости верхних уровней от нижних), чего следует категорически избегать в чистой архитектуре.

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

В ООП задача разграничения архитектурных уровней решается посредством инкапсуляции, абстрагирования и интерфейсов. Это хорошо работает, когда речь идет о вызове процедур и функций. Можно ли то же самое проделать с запросами?

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

Реализация источника (т.е. способ получения определенного набора строк в таблице) скрыта за интерфейсом. Вызывающая сторона не обязана ничего знать о деталях реализации. Это инкапсуляция.

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

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

Механизм виртуальных источников в процессоре схемы запроса позволяет реализовать эту концепцию.

Пример 10

Допустим, на нижнем архитектурном уровне у нас есть сущности ТранспортныеЕдиницы и Автомобили, основанные на одноименных справочниках. Мы хотим скрыть техническую особенность 1С - пометку на удаление. Помеченные элементы не должны появляться в поле зрения.

Определим эти источники как запросы:

 
 Источники нижнего уровня
	ИсточникТранспортныеЕдиницы = 
	"ВЫБРАТЬ
	|	ТранспортныеЕдиницы.Ссылка КАК Ссылка,
	|	ТранспортныеЕдиницы.ПометкаУдаления КАК ПометкаУдаления,
	|	ТранспортныеЕдиницы.КлассТранспорта КАК КлассТранспорта,
	|	ТранспортныеЕдиницы.Наименование КАК Номер,
	|	ТранспортныеЕдиницы.Грузоподъемность КАК Грузоподъемность
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК ТранспортныеЕдиницы
	|ГДЕ
	|	НЕ ТранспортныеЕдиницы.ПометкаУдаления";
	
	ИсточникАвтомобили = 
	"ВЫБРАТЬ
	|	Автомобили.Ссылка КАК Ссылка,
	|	Автомобили.ПометкаУдаления КАК ПометкаУдаления,
	|	Автомобили.Наименование КАК Номер,
	|	Автомобили.ТипКузова КАК ТипКузова,
	|	Автомобили.Марка КАК Марка,
	|	Автомобили.ТранспортнаяЕдиница КАК ТранспортнаяЕдиница
	|ИЗ
	|	Справочник.Автомобили КАК Автомобили
	|ГДЕ
	|	НЕ Автомобили.ПометкаУдаления";

 

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

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

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

 

Здесь мы используем метод процессора УстановитьИсточник() для подключения виртуальных источников. А затем в запросе обращаемся к виртуальным источникам, как к обычным таблицам. Схема запроса принимает их за необъявленные временные таблицы - ну и пускай так думает, до поры.

Кстати, порядок вызовов неважен: можно сначала установить текст запроса, а потом установить источники. Все равно весь анализ процессор произведет в самом конце.

Наконец, на еще более высоком уровне решаем прикладную задачу отбора автотранспорта. Этот запрос будет использовать виртуальный источник Автотранспорт:

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

 

В самом конце мы используем метод ПолучитьКонечныйЗапрос() для получения запроса, который будет непосредственно исполняться. В этот момент происходит встраивание виртуальных источников на всех уровнях вложенности.

И вот какой получается конечный запрос:

 
 Конечный запрос
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Автотранспорт.Ссылка КАК Ссылка,
	|	Автотранспорт.Наименование КАК Номер,
	|	Автотранспорт.Грузоподъемность КАК Грузоподъемность,
	|	ВЫРАЗИТЬ(ЕСТЬNULL(Автомобили.ТипКузова, ЗНАЧЕНИЕ(Справочник.ТипыКузова.ПустаяСсылка)) КАК Справочник.ТипыКузова).Наименование КАК ТипКузова,
	|	ЕСТЬNULL(Автомобили.Марка, ЗНАЧЕНИЕ(Справочник.МаркиАвтомобилей.ПустаяСсылка)) КАК Марка
	|ИЗ
	|	Справочник.ТранспортныеЕдиницы КАК Автотранспорт
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Автомобили КАК Автомобили
	|		ПО (Автотранспорт.Ссылка = Автомобили.ТранспортнаяЕдиница
	|				И НЕ Автомобили.ПометкаУдаления)
	|ГДЕ
	|	Автотранспорт.Грузоподъемность >= &Грузоподъемность
	|	И Автотранспорт.КлассТранспорта = ЗНАЧЕНИЕ(Перечисление.КлассыТранспорта.Автомобильный)
	|	И НЕ Автотранспорт.ПометкаУдаления
	|
	|УПОРЯДОЧИТЬ ПО
	|	Грузоподъемность,
	|	ЕСТЬNULL(Автомобили.ТипКузова, ЗНАЧЕНИЕ(Справочник.ТипыКузова.ПустаяСсылка))";

 

Что мы здесь видим:

  • Каждый виртуальный источник встроился непосредственно по месту его применения (это называется "прямое внедрение").
  • Хотя в виртуальных источниках нижнего уровня выбираются данные таблиц практически без ограничений (пометка удаления не в счет), конечный запрос выглядит эффективным, т.к. учитывает все ограничения, наложенные на более высоких уровнях.
  • Даже разыменование поля, которое в виртуальном источнике реализовано как сложное выражение, конвертировано в синтаксически корректную конструкцию с использованием ВЫРАЗИТЬ.

Красота же?

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

Необходимые условия прямого внедрения

Прежде, чем перейти к определению критериев прямого внедрения, нужно определить некоторые термины:

  • Целевой оператор - оператор, в который встраивается виртуальный источник.
  • Целевой запрос - запрос, которому принадлежит целевой оператор.
  • Запрос-источник - запрос виртуального источника, который встраивается.
  • Оператор-источник - оператор запроса-источника, который встраивается.

Есть два режима прямого внедрения, я не придумал ничего лучше, чем назвать их Режим 1 и Режим 2.

Режим 1 применяется, когда целевой оператор использует только один источник (виртуальный), т.е. не имеет никаких соединений (в т.ч. картезианских). В этом случае должны выполняться следующие критерии:

  • Запрос-источник может содержать ОБЪЕДИНИТЬ, если целевой оператор не содержит свертку*, не содержит ВЫБРАТЬ ПЕРВЫЕ, а целевой запрос в секциях УПОРЯДОЧИТЬ, ИТОГИ и ИНДЕКСИРОВАТЬ использует только имена колонок (не использует выражения полей).
  • Запрос-источник может содержать свертку* в любых своих операторах, если целевой оператор не содержит свертку*, не содержит отбора, а целевой запрос в секциях УПОРЯДОЧИТЬ, ИТОГИ и ИНДЕКСИРОВАТЬ использует только имена колонок.
  • Запрос-источник не содержит ВЫБРАТЬ ПЕРВЫЕ ни в одном из своих операторов.
  • Модификатор запроса-источника ВЫБРАТЬ РАЗРЕШЕННЫЕ разрешен, если целевой запрос также имеет этот модификатор, либо если целевой запрос содержит только 1 оператор (в этом случае модификатор будет перенесен в целевой запрос).
  • Если запрос-источник содержит хотя бы одно ОБЪЕДИНИТЬ (без ВСЕ) или целевой оператор содержит ВЫБРАТЬ РАЗЛИЧНЫЕ и запрос-источник содержит объединение (любого типа), то целевой оператор либо должен быть первым в запросе, либо ему должны предшествовать только операторы ОБЪЕДИНИТЬ (без ВСЕ).

Режим 2 применяется, когда целевой оператор содержит 2 и более источников. В этом случае должны выполняться следующие критерии:

  • Запрос-источник не содержит ОБЪЕДИНИТЬ (т.е. имеет только 1 оператор).
  • Оператор-источник использует хотя бы 1 таблицу (т.е. имеет непустую секцию ИЗ).
  • Оператор-источник не содержит свертку*.
  • Оператор-источник не содержит ВЫБРАТЬ ПЕРВЫЕ.
  • Модификатор запроса-источника ВЫБРАТЬ РАЗРЕШЕННЫЕ разрешен, только если целевой запрос также имеет этот модификатор.

*) Под сверткой понимается: наличие группировок, использование агрегатных функций (даже без группировок), использование ВЫБРАТЬ РАЗЛИЧНЫЕ (т.к. это частный случай группировки).

В запросе-источнике игнорируются: ДЛЯ ИЗМЕНЕНИЯ, УПОРЯДОЧИТЬ, ИТОГИ, ИНДЕКСИРОВАТЬ, а также любые элементы компоновки данных (всё, что в тексте запроса пишется в фигурных скобках).

Хотя упорядочивание и игнорируется, но оно, тем не менее, может пригодиться в запросе виртуального источника. Если источник не отвечает критериям прямого внедрения, он будет встроен как временная таблица. А в этом случае секция УПОРЯДОЧИТЬ ПО будет автоматически конвертирована в ИНДЕКСИРОВАТЬ ПО. Это такой хитрый способ задать индексирование там, где оно не разрешено правилами языка запросов.

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

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

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

  • Текст запроса
  • Объект Запрос. Этот может содержать, кроме текста запроса, еще и собственные внутренние параметры, и даже использовать временные таблицы из собственного менеджера временных таблиц.
  • Объект ТаблицаЗначений или РезультатЗапроса. Этот всегда встраивается в виде временной таблицы, создаваемой из параметра (соответствующий параметр добавляется автоматически).
  • ОбработкаОбъект.ПроцессорСхемыЗапроса. Экземпляр должен быть подготовлен к финальному вызову метода ПолучитьКонечныйЗапрос(). Причем в данном случае применяется "ленивый" подход: этот метод будет вызван в самый последний момент, при формировании верхнеуровневого конечного запроса.

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

Пример 11

Виртуальные источники не были бы полноценными, если бы не могли иметь параметров.

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

Определим его следующим образом:

 
 Источник КурсыВалют
	ИсточникКурсыВалют =
	"ВЫБРАТЬ
	|	КурсыВалют.Период КАК Период,
	|	КурсыВалют.Валюта КАК Валюта,
	|	КурсыВалют.Курс КАК Курс
	|ИЗ
	|	РегистрСведений.КурсыВалют.СрезПоследних(&Дата, &Отбор) КАК КурсыВалют";

 

Источник имеет параметры Дата и Отбор, которые мы декларируем в его интерфейсе. Это будут его внешние параметры.

Теперь используем наш виртуальный источник КурсыВалют в запросе:

 
 Прикладной запрос
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Период КАК Период,
	|	Продажи.СуммаВал КАК СуммаВал,
	|	ВЫРАЗИТЬ(Продажи.СуммаВал * КурсыВалют.Курс КАК Число(15, 2)) КАК Сумма
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ КурсыВалют КАК КурсыВалют
	|		ПО Продажи.Валюта = КурсыВалют.Валюта";
	
	Запрос = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьИсточник("КурсыВалют", ИсточникКурсыВалют)
			.ПараметрИсточника("Дата", "&ДатаКурса")
			.ПараметрИсточника("Отбор", "Валюта В (ВЫБРАТЬ NULL)")
				.ВложенныйВыбрать().Различные().ИзТаблицы("РегистрСведений.Продажи").Поле("Валюта")
		.УстановитьТекстЗапроса(ТекстЗапроса)
		.ПолучитьКонечныйЗапрос();

 

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

  • Параметр Дата задан простым выражением "&ДатаКурса", которое ссылается уже на параметр нашего прикладного запроса.
  • Параметр Отбор задан выражением, содержащим вложенный запрос. И, как видим, в этом контексте процессор также поддерживает динамическое конструирование запроса (хотя можно было задать его и просто текстом, но захотелось повыпендриваться).

И вот такой мы получаем конечный запрос:

 
 Конечный запрос
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Период КАК Период,
	|	Продажи.СуммаВал КАК СуммаВал,
	|	ВЫРАЗИТЬ(Продажи.СуммаВал * КурсыВалют.Курс КАК ЧИСЛО(15, 2)) КАК Сумма
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(
	|				&ДатаКурса,
	|				Валюта В
	|					(ВЫБРАТЬ РАЗЛИЧНЫЕ
	|						РегистрСведенийПродажи.Валюта КАК Валюта
	|					ИЗ
	|						РегистрСведений.Продажи КАК РегистрСведенийПродажи)) КАК КурсыВалют
	|		ПО (Продажи.Валюта = КурсыВалют.Валюта)";

 

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

Пример 12

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

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

 
 Источник КурсыВалют
	ИсточникКурсыВалют =
	"ВЫБРАТЬ
	|	КурсыВалют.Период КАК Период,
	|	КурсыВалют.Валюта КАК Валюта,
	|	КурсыВалют.Курс КАК Курс
	|ИЗ
	|	РегистрСведений.КурсыВалют КАК КурсыВалют
	|ГДЕ
	|	КурсыВалют.Валюта = &Валюта
	|	И КурсыВалют.Период = НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ)";

 

И используем его следующим образом:

 
 Прикладной запрос
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Период КАК Период,
	|	Продажи.СуммаВал КАК СуммаВал,
	|	ВЫРАЗИТЬ(Продажи.СуммаВал * КурсыВалют.Курс КАК Число(15, 2)) КАК Сумма
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ КурсыВалют КАК КурсыВалют
	|		ПО ИСТИНА";
	
	Запрос = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьИсточник("КурсыВалют", ИсточникКурсыВалют)
			.ПараметрИсточника("Дата", "Продажи.Период")
			.ПараметрИсточника("Валюта", "Продажи.Валюта")
		.УстановитьТекстЗапроса(ТекстЗапроса)
		.ПолучитьКонечныйЗапрос();

 

Здесь мы задали значения параметров в виде контекстно-зависимых выражений. В результате получим такой конечный запрос:

 
 Конечный запрос
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Период КАК Период,
	|	Продажи.СуммаВал КАК СуммаВал,
	|	ВЫРАЗИТЬ(Продажи.СуммаВал * КурсыВалют.Курс КАК ЧИСЛО(15, 2)) КАК Сумма
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
	|		ПО (КурсыВалют.Валюта = Продажи.Валюта
	|				И КурсыВалют.Период = НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ))";

 

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

  • В запросе виртуального источника такие параметры не должны использоваться во вложенном запросе (который в секции "ИЗ"), т.к. по правилам языка запросов он изолирован от пространства имен внешнего контекста. Произойдет ошибка.
  • Такие параметры не должны использоваться в параметрах виртуальных таблиц (т.к. по сути этот тот же вложенный запрос).
  • Если виртуальный источник будет встроен как временная таблица, использование таких параметров также приведет к ошибке, т.к. запрос создания временной таблицы будет вне контекста целевого оператора.

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

Пример 13

Сделаем уже нормальный источник КурсыВалют, который может давать нам курсы одновременно на множество разных дат и разных валют.

 
 Источник КурсыВалют (продвинутый)
	ИсточникКурсыВалют =
	"ВЫБРАТЬ
	|	КурсыВалютКлючи.Период КАК Период,
	|	КурсыВалютКлючи.Валюта КАК Валюта,
	|	КурсыВалют.Курс КАК Курс
	|ИЗ
	|	РегистрСведений.КурсыВалют КАК КурсыВалют
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
	|			ВЫБРАТЬ
	|				ТаблицаОтбора.Валюта КАК Валюта,
	|				ТаблицаОтбора.Период КАК Период,
	|				МАКСИМУМ(КурсыВалют.Период) КАК ПериодКлюча
	|			ИЗ
	|				РегистрСведений.КурсыВалют КАК КурсыВалют
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаОтбора КАК ТаблицаОтбора
	|					ПО КурсыВалют.Валюта = ТаблицаОтбора.Валюта
	|						И КурсыВалют.Период <= ТаблицаОтбора.Период
	|			СГРУППИРОВАТЬ ПО
	|				ТаблицаОтбора.Валюта,
	|				ТаблицаОтбора.Период
	|		) КАК КурсыВалютКлючи
	|		ПО КурсыВалют.Период = КурсыВалютКлючи.ПериодКлюча
	|			И КурсыВалют.Валюта = КурсыВалютКлючи.Валюта";

 

Видим, что в запросе появилась ТаблицаОтбора. Это тоже параметр виртуального источника, но на этот раз табличного типа. И вот как мы его используем:

 
 Прикладной запрос
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Период КАК Период,
	|	Продажи.СуммаВал КАК СуммаВал,
	|	ВЫРАЗИТЬ(Продажи.СуммаВал * КурсыВалют.Курс КАК Число(15, 2)) КАК Сумма
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ КурсыВалют КАК КурсыВалют
	|		ПО Продажи.Валюта = КурсыВалют.Валюта
	|			И Продажи.Период = КурсыВалют.Период";
	
	Запрос = Обработки.ПроцессорСхемыЗапроса.Создать()
		.УстановитьТекстЗапроса(ТекстЗапроса)
		.УстановитьИсточник("КурсыВалют", ИсточникКурсыВалют)
			.ПараметрИсточника("ТаблицаОтбора",
				"ВЫБРАТЬ РАЗЛИЧНЫЕ
				|	Продажи.Период КАК Период,
				|	Продажи.Валюта КАК Валюта
				|ИЗ
				|	РегистрСведений.Продажи КАК Продажи
				|УПОРЯДОЧИТЬ ПО
				|	Период,
				|	Валюта")
		.ПолучитьКонечныйЗапрос();

 

В качестве значения параметра мы передали текст запроса. Но это не единственно возможный вариант. На самом деле, для виртуального источника табличный параметр является… тоже виртуальным источником. Называется "виртуальный источник 2-го порядка". И может быть задан любым типом виртуального источника.

В результате получим вот такой конечный запрос:

 
 Конечный запрос
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Продажи.Период КАК Период,
	|	Продажи.Валюта КАК Валюта
	|ПОМЕСТИТЬ ТаблицаОтбора_1
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Период,
	|	Валюта
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Продажи.Номенклатура КАК Номенклатура,
	|	Продажи.Валюта КАК Валюта,
	|	Продажи.Период КАК Период,
	|	Продажи.СуммаВал КАК СуммаВал,
	|	ВЫРАЗИТЬ(Продажи.СуммаВал * КурсыВалют.Курс КАК ЧИСЛО(15, 2)) КАК Сумма
	|ИЗ
	|	РегистрСведений.Продажи КАК Продажи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
	|				ТаблицаОтбора.Валюта КАК Валюта,
	|				ТаблицаОтбора.Период КАК Период,
	|				МАКСИМУМ(КурсыВалют.Период) КАК ПериодКлюча
	|			ИЗ
	|				РегистрСведений.КурсыВалют КАК КурсыВалют
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаОтбора_1 КАК ТаблицаОтбора
	|					ПО (КурсыВалют.Валюта = ТаблицаОтбора.Валюта
	|							И КурсыВалют.Период <= ТаблицаОтбора.Период)
	|			
	|			СГРУППИРОВАТЬ ПО
	|				ТаблицаОтбора.Валюта,
	|				ТаблицаОтбора.Период) КАК КурсыВалютКлючи
	|			ПО (КурсыВалют.Период = КурсыВалютКлючи.ПериодКлюча
	|					И КурсыВалют.Валюта = КурсыВалютКлючи.Валюта)
	|		ПО (Продажи.Валюта = КурсыВалютКлючи.Валюта
	|				И Продажи.Период = КурсыВалютКлючи.Период)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ ТаблицаОтбора_1";

 

Здесь мы видим пример того, как виртуальный источник встроился в виде временной таблицы (это не потому, что он передан как параметр, а потому, что содержит ВЫБРАТЬ РАЗЛИЧНЫЕ, см. критерии прямого внедрения). И видим, как УПОРЯДОЧИТЬ ПО превратилось в ИНДЕКСИРОВАТЬ ПО.

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

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

Процессору важно понимать, имеет ли он дело с локальным или с нелокальным запросом в табличном параметре. По умолчанию запрос, заданный обычным текстом, считается локальным. Чтобы объявить его нелокальным, у метода ПараметрИсточника есть опциональный параметр ЗначениеЛокально, который нужно установить в Ложь. Если же табличный параметр задан как объект Запрос или экземпляр процессора, то он всегда считается нелокальным.

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

 

Виртуальные источники как альтернатива динамическому конструированию

Впрочем, даже если вас не волнуют вопросы чистой архитектуры (да-да, где 1С и где архитектура), виртуальным источникам можно найти применение. Например, во многих случаях они могут служить альтернативой динамическому "допиливанию" запросов.

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

 

Опции процессора схемы запроса

У процессора есть несколько опций, сделанных в виде реквизитов типа Булево. Все они сейчас устанавливаются по умолчанию в состояние Истина.

  • АвтодобавлениеДоступныхПолей

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

  • ТипизацияПолейВременныхТаблиц

При использовании метода ВыбратьИзПараметра() процессор автоматически типизирует поля временной таблицы через конструкцию ВЫРАЗИТЬ. Работа этого режима была продемонстрирована в примере 7. Если этого не требуется делать по умолчанию, режим можно отключить.

  • КонкатенацияУсловийСоединений

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

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

  • ИзолированнаяОбработкаВложенныхЗапросов

Данный режим включается для обхода еще одного глюка схемы запроса, связанного с обработкой вложенных запросов в секции "ИЗ" (не путать с вложенными запросами выражений). В этом режиме вложенный запрос обрабатывается в отдельном экземпляре схемы запроса, несмотря на то, что номинально схема позволяет работать с ним напрямую. Это несколько замедляет обработку вложенного запроса, т.к. приходится перегонять его в текст, потом в схему, а потом все в обратном порядке. Но отключать этот режим не рекомендуется.

 

Выявленные ошибки схемы запроса

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

Кстати, многие их этих ошибок воспроизводятся в интерактивном конструкторе запросов в конфигураторе.

1. Схема самовольно добавляет соединения при добавлении новых источников в оператор.

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

2. Невозможно добавить доступное поле составного типа.

В объекте ОписаниеВременнойТаблицыСхемыЗапроса мы можем добавлять доступные поля. Но в коллекции ДоступныеПоляСхемыЗапроса метод Добавить(Имя, Тип) ожидает 2-м параметром, внезапно, объект Тип, вместо ОписаниеТипов (хотя в самом поле тип представлен объектом ОписаниеТипов). Очевидная ошибка проектирования, но она существует уже столько, сколько существует схема запроса. В результате нет возможности создать доступное поле составного типа. Более того, нельзя даже корректно создать поле типа Строка, т.к. получится строка неограниченной длины, что далее приведет к ошибкам сравнения.

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

3. Потеря соединений при модификации вложенного запроса.

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

4. Потеря полей выборки при замене таблицы на доступную временную таблицу.

В коллекции ИсточникиСхемыЗапроса есть метод Заменить, позволяющий в источнике типа ТаблицаСхемыЗапроса заменить одну доступную таблицу на другую. Замена на таблицы ИБ проходит как положено. А вот при попытке заменить на доступную временную таблицу, пускай и с полностью идентичной структурой полей, происходит потеря всех полей в коллекции ВыбираемыеПоля. Способ обхода пока не искал, но напрашивается сохранение всех полей выборки с последующим их восстановлением. Хотя там не все так просто, если запрос содержит группировки или участвует в объединении. В текущей версии процессор просто выдает исключение при попытке выполнить такую замену.

5. Потеря полей выборки при замене имени таблицы в описании временной таблицы.

Баг похож на предыдущий, но связан с использованием объекта ОписаниеВременнойТаблицыСхемыЗапроса. Если динамически сконструировать запрос создания временной таблицы из параметра (то, что делает метод процессора ВыбратьИзПараметра), переместить этот запрос в нужную позицию в пакете, а затем попытаться изменить свойство ИмяТаблицы (например, с "&ВТ" на "&ВТ_1"), происходит потеря всех полей в коллекции ВыбираемыеПоля.

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

6. Ошибка при добавлении соединения

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

Первое соединение группы добавится без проблем, а вот при добавлении 2-го схема запроса выдаст ошибку "Противоречивая связь". Это происходит в случае, когда тип 1-го соединения отличается от ЛевоеВнешнее. Предполагаемая причина: все новые соединения создаются по умолчанию с типом ЛевоеВнешнее, тип соединения можно изменить только после его создания. А тип группы соединений определяется типом 1-го соединения. Понятно, что когда мы в группу соединений с типом Внутреннее пытаемся добавить ЛевоеВнешнее, схема запроса видит противоречие. Но при этом нет никакого способа указать тип соединения непосредственно в момент его добавления.

Напрашивался такой путь обхода: перед добавлением нового соединения установить тип 1-го соединения группы в ЛевоеВнешнее, а после добавления - восстановить исходный тип. Но, как показали тесты, это работает от случая к случаю. До исправления бага рекомендуется всегда включать режим КонкатенацияУсловийСоединений. Что, снова-таки, увеличивает время выполнения.

7. Ошибки при переименовании псевдонима таблицы.

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

Если 1С когда-нибудь исправит все эти баги, можно будет сократить затраты времени на внедрение виртуального источника на 10-15% а при использовании вложенных запросов - и того больше. По тем багам, где удалось выявить четкие условия воспроизведения, я отправил багрепорты в 1С - будем ждать.

 

Несколько слов о производительности

Замеры производительности выполнялись на машине с такими параметрами: Intel(R) Core(TM) i5-10500 CPU @ 3.10GHz, 16,0 ГБ ОЗУ, Win 10, 1С 8.3.27.

Систематизированного нагрузочного тестирования пока не выполнялось. Прогон набора юнит-тестов показывает время выполнения отдельного теста в диапазоне 1-80 мс (без режима отладки разумеется). Разброс обусловлен разной сложностью тестов. На основе имеющейся статистики можно утверждать следующее:

  • Примеры со структурной обработкой (без вложенных запросов, без виртуальных источников) - единицы мс.
  • Примеры с виртуальными источниками, без вложенных запросов - первые десятки мс.
  • Примеры, сочетающие виртуальные источники с вложенными запросами - до 80 мс и выше.

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

 

Форма поставки и требования к платформе

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

Процессор использует регулярные выражения, поэтому теоретически минимальная версия платформы - 8.3.23. Но практически не тестировался ниже 8.3.27.

Вступайте в нашу телеграмм-группу Инфостарт

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

См. также

Инструментарий разработчика Чистка данных Свертка базы Инструменты администратора БД Системный администратор Программист Руководитель проекта 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Инструмент представляет собой обработку для проведения свёртки или обрезки баз данных. Работает на ЛЮБЫХ конфигурациях (УТ, БП, ERP, УНФ, КА и т.д.). Поддерживаются серверные и файловые базы, управляемые и обычные формы. Может выполнять свертку одновременно в несколько потоков. А так же автоматически, без непосредственного участия пользователя. Решение в Реестре отечественного ПО

24900 руб.

20.08.2024    60102    320    160    

289

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта 1С:Предприятие 8 Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

16500 руб.

02.09.2020    244823    1341    419    

1122

Инструментарий разработчика Нейросети Платные (руб)

Первые попытки разработки на 1С с использованием больших языковых моделей (LLM) могут разочаровать. LLMки сильно галлюцинируют, потому что не знают устройства конфигураций 1С, не знают нюансов синтаксиса. Но если дать им подсказки с помощью MCP, то результат получается кардинально лучше. Далее в публикации: MCP для поиска по метаданым 1С, справке синтакс-помошника и проверки синтаксиса.

15250 руб.

25.08.2025    41218    81    22    

95

Пакетная печать Печатные формы Инструментарий разработчика Программист 1С:Предприятие 8 1С:Зарплата и кадры бюджетного учреждения 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Библиотека стандартных подсистем Платные (руб)

Расширение для создания и редактирования печатных форм в системе 1С:Предприятие 8.3. Благодаря конструктору можно значительно снизить затраты времени на разработку печатных форм, повысить качество и прозрачность разработки, а также навести порядок в многообразии корпоративных печатных форм.

22570 руб.

06.10.2023    35253    90    40    

112

Инструментарий разработчика Программист 1С:Предприятие 8 Платные (руб)

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

9500 руб.

17.05.2024    49329    169    63    

205

Инструменты администратора БД Инструментарий разработчика Роли и права Программист 1С:Предприятие 8 1C:Бухгалтерия Россия Платные (руб)

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

17000 руб.

10.11.2023    23580    84    42    

99

Разработка Инструментарий разработчика Работа с интерфейсом Адаптация типовых решений Нейросети 1C:Бухгалтерия 1C:ERP 1С:ЗУП 1С:КА 1С:УНФ 1С:УТ 1С:Розница 1С:ДО 1С:ERP Управление предприятием 2 Платные (руб)

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

36600 руб.

28.08.2025    6209    2    2    

6
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SergMuravev 877 19.02.26 11:28 Сейчас в теме
Пожелание: прячьте код под сворачиваемые блоки. Еще для лонгридов нужно разбиение на логические блоки и оглавление с якорями. Когнитивная сложность превышена.
Пока вижу, что явно полезно, но плохо читаемо.
ixijixi; starik-2005; SerVer1C; +3 Ответить
6. korvintorson 36 19.02.26 12:44 Сейчас в теме
2. DmitryKlimushkin 136 19.02.26 11:55 Сейчас в теме
Во-2х, схема запроса не умеет строить объектную модель вложенных запросов выражений (запрос в операторе "В (ВЫБРАТЬ …)").

Это совсем не так. Нет никакого отдельного механизма по формированию встроенных запросов. Он хоть и встроенный, но такой же запрос и что мешает иметь два или более объекта "СхемаЗапроса", в одном из которых легко описывается встроенный запрос?

"шибко умное" автоматическое добавление соединений при добавлении источников, совершенно ненужное при программном формировании запроса, но при этом неотключаемое.

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

Схему Запроса использую часто и с удовольствием. Шибко удивляюсь, когда в тексте типовых конфигураций вижу механизм работы с текстом запроса через метод "СтрЗаменить")
Не очень понимаю - зачем бы мне пригодился описанный процессор. В самом объекте есть всё, что нужно и пользоваться этим совершенно несложно.
4. qwinter 683 19.02.26 12:28 Сейчас в теме
(2)
Шибко удивляюсь, когда в тексте типовых конфигураций вижу механизм работы с текстом запроса через метод "СтрЗаменить")
Нужен тест скорости.
tormozit; +1 Ответить
5. DmitryKlimushkin 136 19.02.26 12:36 Сейчас в теме
(4) Есть скорость исполнения кода, а есть скорость его "обслуживания" программистом. Не случись какие-то изменения в запрос вносить - сколько надо глазками по строкам бегать, выискивая все эти "заменяемые фрагменты".
16. korvintorson 36 19.02.26 14:37 Сейчас в теме
(4) Предположу, что в большинстве случаев разница будет неощутима на фоне времени выполнения самого запроса.
7. SerVer1C 1024 19.02.26 12:45 Сейчас в теме
(2) Вот как раз иногда удобнее и в разы быстрее заменить кусок текста запроса, чем в *цать строк кода пытаться добраться до нужных частей запроса через схему.
Замена текста быстрее, но модификация через схему нагляднее.
bulpi; tormozit; +2 Ответить
9. DmitryKlimushkin 136 19.02.26 12:47 Сейчас в теме
(7) Если запросы несложные - наверное.
11. korvintorson 36 19.02.26 13:01 Сейчас в теме
(7) Иногда да, а иногда нет четких и однозначных критериев поиска нужного места в тексте, если запрос пакетный или содержит вложенные запросы.

Есть еще вариант с использованием псевдо-параметров. Когда в текст запроса заранее вставляется что-нибудь типа "ГДЕ &_УсловияОтбора_", а потому туда подставляется текст через СтрЗаменить. Сам этим подходом постоянно пользовался. Но это удобно только в тех местах, где синтаксически разрешено использование параметра, иначе запрос становится недоступным для работы через конструктор. Кроме того, требуется, чтобы запрос был заранее подготовлен к такому действию. Т.е. нужно заранее ограничить круг задач, в которых этот запрос может применяться. Это зачастую неудобно.
10. korvintorson 36 19.02.26 12:54 Сейчас в теме
(2)
Это совсем не так. Нет никакого отдельного механизма по формированию встроенных запросов.


Есть два вида вложенных запросов: запросы-источники в секции "ИЗ" и запросы выражений в операторе "В". Первые схема запроса понимает, для этого предусмотрен объект ВложенныйЗапросСхемыЗапроса, внутри которого содержится ЗапросВыбораСхемыЗапроса.
Вторые схема "не видит", для нее это просто текст в составе выражения.


(2)
и что мешает иметь два или более объекта "СхемаЗапроса", в одном из которых легко описывается встроенный запрос?


Легко на словах, пока сами не попробуете. Как вы решите проблему использования полей из контекста вышестоящего запроса? Если вырезать вложенный запрос выражения, то поля вышестоящего контекста превратятся в неизвестные поля. При попытке скормить этот текст схеме запроса вы получите ошибку.


(2)
Насчёт автоматического соединения - правда, есть такое сомнительное "удобство", подаренное платформой. Поэтому я, при добавлении источников, принудительно очищаю коллекции соединений


Ну так об этом и речь. Автоматическое создание соединений невозможно отключить.
12. DmitryKlimushkin 136 19.02.26 13:02 Сейчас в теме
(10)
Вторые схема "не видит", для нее это просто текст в составе выражения

Я про это и писал, упоминая второй объект СхемаЗапроса. В нём формируется тот самый текст, который потом и станет частью выражения

Как вы решите проблему использования полей из контекста вышестоящего запроса?

Вот тут я не понял. А как это вообще может быть использовано? У меня есть "нижестоящий" запрос, вложенный или часть условия - не суть. В этом запросе свои источники, свои псевдонимы таблиц. Разве в этом запросе как-то можно упомянуть поле вышестоящего запроса? Я никак вопрос в голове не уложу, чего-то не понимаю?
14. korvintorson 36 19.02.26 13:06 Сейчас в теме
(12)
Разве в этом запросе как-то можно упомянуть поле вышестоящего запроса?


Можно, см. Пример 8. Синтаксис языка это допускает, и при выполнении запроса это работает именно так, как ожидается.
3. aximo 2587 19.02.26 12:20 Сейчас в теме
это мы знаем, что примечательно данные модули я обнаруживал только в ут/ка/erp - так и называются "схемы запросов"...

предполагаю, что данные возможности "работают" не на полную мощность еще.... ознакомиться с практикой можно здесь https://infostart.ru/1c/articles/2498543/
8. SerVer1C 1024 19.02.26 12:47 Сейчас в теме
(3) я так понял, что фирма 1с ещё не выработала единую схему для управления запросами через АПИ (БСП), поэтому в разных конфах встречаются свои велосипеды.
15. aximo 2587 19.02.26 13:21 Сейчас в теме
(8) в бсп этого нет… а я вообще сторонник классических методов доработки запросов
17. korvintorson 36 19.02.26 14:46 Сейчас в теме
(3) Ознакомился. И как по-вашему, какое из решений удобнее?

// === Левое соединение с таблицей контактная информация ====
	
ОписаниеТаблицы = СхемыЗапросов.ОписаниеТаблицы(
					Тип("ТаблицаСхемыЗапроса"),
					"Справочник.Контрагенты.КонтактнаяИнформация",
					"КонтрагентыКонтактнаяИнформация",
					"Ссылка");
				
ОписаниеСоединения = СхемыЗапросов.ОписаниеСоединения(
					ОписаниеТаблицы,
					"КонтрагентыКонтактнаяИнформация.Ссылка = &Ссылка",
					ТипСоединенияСхемыЗапроса.ЛевоеВнешнее);
				
ТекстЗапроса =  СхемыЗапросов.ДобавитьСоединениеВЗапрос(
					НовыйТекстЗапроса2,
					"Контрагенты",
					ОписаниеСоединения,
					0);
Показать


ТекстЗапроса = Обработки.ПроцессорСхемыЗапроса().Создать().УстановитьТекст(НовыйТекстЗапроса2)
  .Таблица("Контрагенты")
  .ЛевоеСодединение().ИзТаблицы("Справочник.Контрагенты.КонтактнаяИнформация", "КонтрагентыКонтактнаяИнформация")
  .ПоУсловию("КонтрагентыКонтактнаяИнформация.Ссылка = &Ссылка")
  .ПолучитьТекстЗапроса();
24. aximo 2587 19.02.26 17:06 Сейчас в теме
(17) ну это уже вопрос к типовым... если у вас есть возможность - задайте его на спец.форуме франчей....
26. korvintorson 36 19.02.26 17:56 Сейчас в теме
(24) А смысл? Как сделали - так сделали, чего уж там.
Не все вспоминают о том, что обработки в 1С - это вполне себе ООП-инструмент, при помощи которого можно делать много чего интересного. Если б в 1С были еще абстрактные интерфейсы и наследование, эх...
13. DmitryKlimushkin 136 19.02.26 13:05 Сейчас в теме
А вообще, это очень напоминает землекопа, который перестал копать землю и занялся полировкой и тюнингом лопаты))) (без обид!!)
Столько всего можно и нужно написать, а пишется только это))
18. korvintorson 36 19.02.26 14:49 Сейчас в теме
(13) Вы намекаете на то, что люди тратят свое время на решение своих задач вместо решения ваших?
19. DmitryKlimushkin 136 19.02.26 14:54 Сейчас в теме
(18) Моих?? А какие у меня могут быть задачи??
Я бы сказал, что у нас крайне мало красивых и полноценных отраслевых решений, просто поле непаханое.
В самой типовой Эске (БП) столько всего стоит переписать или дописать - на месяцы работа уже есть.
Я про такие задачи, не мои - общие.
20. korvintorson 36 19.02.26 14:57 Сейчас в теме
(19) Задачи общие, а как доходит до денег - так они почему-то у каждого свои))
Что до отраслевых решений: я вот, например, занимаюсь отраслевым решением для логистики, мне работы хватает. Пусть каждый занимается своим делом.
starik-2005; +1 Ответить
21. DmitryKlimushkin 136 19.02.26 14:59 Сейчас в теме
(20) Логистика? Мои соболезнования)) Творческая работа очень тяжело оцифровывается, могу только представить...
22. korvintorson 36 19.02.26 15:24 Сейчас в теме
(21) Да, есть такое. Особенно в мультимодалке.
23. kalyaka 1146 19.02.26 16:55 Сейчас в теме
Интересные идеи. Тоже думал про виртуальные источники, но я их "зашивал" в модель. Про то, что их можно зашить в текст я не подумал )
25. korvintorson 36 19.02.26 17:50 Сейчас в теме
(23) Вижу, вы начали копать эту тему с 18-го года. Эволюция от общего модуля до обработки с текучим интерфейсом. Бегло посмотрел, у вас есть интересные "шорткаты", типа метода Связь(). Я не заморачивался и сосредоточился на задаче 100% охвата объектной модели запроса, по принципу "все возможности схемы должны быть доступны, и ничего лишнего".
На самом деле, у меня именно виртуальные источники были первичной задачей, а вся эта динамическая генерация запроса получилась как побочный эффект, сначала отдельные элементы, ну а потом уже решил доработать ее до полноценного интерфейса.
27. kalyaka 1146 19.02.26 18:41 Сейчас в теме
(25) у меня исходно была задача работы с динамическим запросом. Затем я стал вводить "шорткаты" и это стало удобным для анализа сложных запросов. Сложный запрос перевожу в модель с шорткатами, еще немного добавляю группировок: источники, связи, отборы, период, измерения, ресурсы, реквизиты - и вот уже сложный запрос становится структурированным.

Сейчас у меня появилась потребность обратить внимание на модификацию запросов и я готовлю новую версию модели с командами позиционирования и модификации. Планирую включить помимо "достроичных" команд и такие как: заменить оператор, отбор, соединение.
28. korvintorson 36 19.02.26 18:49 Сейчас в теме
(27)
Сейчас у меня появилась потребность обратить внимание на модификацию запросов и я готовлю новую версию модели с командами позиционирования.

Ерунда, я уложился в какие-то полсотни функций))
29. kalyaka 1146 19.02.26 19:11 Сейчас в теме
(28) ну если меряться, то у меня за сотню будет ))

Тут еще вопрос в том, как все это помнить? У меня есть конструктор, в котором можно из запроса получить модель и обратно
30. korvintorson 36 19.02.26 19:57 Сейчас в теме
(29) Так я же имел в виду только функции навигации. Так-то и у меня за сотню, и это только в программном интерфейсе.

А запомнить не сложнее, чем запомнить язык запросов или объектную модель.
Я специально старался, чтобы имена методов навигации были созвучны именам объектов модели, а имена конструкторов - ключевым словам языка запросов.
При этом сведено к минимуму количество параметров в методах (их труднее запомнить), в пользу увеличения количества методов.
31. korvintorson 36 19.02.26 20:01 Сейчас в теме
(29) Кстати, примите во внимание. Развитая система методов навигации не только упрощает реализацию конструкторов, но и делает их менее чувствительными к точности позиционирования. Что, в свою очередь, сокращает необходимость в использовании явного позиционирования.
32. bulpi 218 19.02.26 21:49 Сейчас в теме
Красиво...
Напомнило мне анекдот про алкоголика, йогурт и портвейн :)
33. kalyaka 1146 22.02.26 17:29 Сейчас в теме
В объекте ОписаниеВременнойТаблицыСхемыЗапроса мы можем добавлять доступные поля. Но в коллекции ДоступныеПоляСхемыЗапроса метод Добавить(Имя, Тип) ожидает 2-м параметром, внезапно, объект Тип
Вот это да, эпично. В доступном поле поддерживается тип значения типа ОписаниеТипов, а добавить поле можно только по одному типу. У этого следующие последствия: при описании временной таблицы приходится прописывать 1-й тип из типа значения, но можно и Неопределено ставить. Последствие этого в том, что нельзя будет обращаться к полям этого поля через точку, а также нельзя использовать оператор ССЫЛКА. В 1-ом случае просто нужно сделать соединение с таблицей, а во 2-ом использовать функцию ТипЗначения().
Более того, нельзя даже корректно создать поле типа Строка, т.к. получится строка неограниченной длины, что далее приведет к ошибкам сравнения.
Раньше действительно это приводило к ошибке. Попробовал сейчас проверить на платформе 8.3.27.1989 и ошибка больше не возникает! Т.е. никаких ограничений по длине строки во временной таблице теперь нет.
36. korvintorson 36 22.02.26 20:35 Сейчас в теме
(33) А можете привести здесь пример, на котором вам удалось избежать ошибки "Нельзя сравнивать поля неограниченной длины и поля несовместимых типов"?
38. kalyaka 1146 22.02.26 20:59 Сейчас в теме
(36) ок, я не так вспомнил. Мне показалось, что ошибка была типа нельзя создать временную таблицу с полем неограниченной длины. Ну тогда, если поле Строка, то в ДопустимыеПоля.Добавить(Поле, ?(Тип = Тип("Строка"), Неопределено, Тип)
40. korvintorson 36 22.02.26 22:45 Сейчас в теме
(38) Так сейчас и приходится делать, да.
34. kalyaka 1146 22.02.26 17:49 Сейчас в теме
схема запроса автоматически раскладывает такое соединение на несколько элементов СоединениеИсточникаЗапросаСхемыЗапроса
Я просто отделил само соединение и условия к соединениям. Вначале указывается соединение между источниками и тип соединения, а затем к нему условия
37. korvintorson 36 22.02.26 20:43 Сейчас в теме
(34) Пока что единственный надежный способ - это пихать все условия одного соединения в одно выражение. Но.
После того, как вы это выгрузите в текст запроса, а потом этот текст заново скормите схеме запроса, то она снова разложит сложное условие на несколько объектов. А в процессоре схемы такие преобразования схема->текст->схема довольно часто происходят, т.к. он активно использует дополнительные экземпляры схемы при работе с вложенными запросами и виртуальными источниками. Ну и вообще работа обычно начинается с инициализации схемы текстом, если только запрос не генерируется с нуля. Поэтому, после инициализации схемы текстом запроса, если включен режим КонкатенацияУсловийСоединений, отрабатывает специальная процедура, которая пробегается по всей схеме и выполняет склейку условий в групповых соединениях.
39. kalyaka 1146 22.02.26 21:02 Сейчас в теме
(37) я группирую соединения по ключу ПсевдонимСлева, ПсевдонимСправа, ТипСоединения
35. kalyaka 1146 22.02.26 18:16 Сейчас в теме
Прогон набора юнит-тестов показывает время выполнения отдельного теста в диапазоне 1-80 мс
У меня примерно такие же прикидки. Ну конечно все это сильно зависит от машины и окружения, поэтому желательно знать относительную производительность. Так по моим прикидкам простые манипуляции с текстом - это десятки микросекунд, а со схемой - 0.5 до единиц миллисекунд, т.е. разница в 10-100 раз. Однако если текстом моделировать схему запроса, то тут уже схема выигрывает. Т.е. сложные манипуляции с текстом начинают проигрывать схеме.
Общий вывод я для себя сделал такой. Область применения схемы:
1. динамические запросы
2. точечные "инъекции" для изменения типовых запросов, если нет повышенных требований к производительности
3. моделирование сложных запросов в период разработки, а на прод помещать уже готовый текст запроса
4. внешние обработки, которые часто требуется изменять. Поддерживать схему удобнее, т.к. она позволяет алгоритмически собирать запросы любой сложности
41. kalyaka 1146 23.02.26 20:20 Сейчас в теме
Схема запроса дает возможность копирования запросов и операторов
Запрос можно скопировать, а вот оператор - такого я не нашел. Хотя интерактивно такая операция есть и логично предположить, что и в схеме должна быть. Или копирование оператора в процессоре сделано программно?
43. korvintorson 36 23.02.26 20:25 Сейчас в теме
(41) Посмотрите внимательно на метод Добавить в коллекции операторов.
44. kalyaka 1146 23.02.26 20:28 Сейчас в теме
(43) Спасибо! Ну да, прямо совсем не очевидно :)
45. korvintorson 36 23.02.26 20:30 Сейчас в теме
(44) Мне вообще иногда кажется, что разные части схемы запроса проектировали разные люди.
42. kalyaka 1146 23.02.26 20:20 Сейчас в теме
Прогон набора юнит-тестов показывает время выполнения отдельного теста в диапазоне 1-80 мс
У меня примерно такие же прикидки. Ну конечно все это сильно зависит от машины и окружения, поэтому желательно знать относительную производительность. Так по моим прикидкам простые манипуляции с текстом - это десятки микросекунд, а со схемой - 0.5 до единиц миллисекунд, т.е. разница в 10-100 раз. Однако если текстом моделировать схему запроса, то тут уже схема выигрывает. Т.е. сложные манипуляции с текстом начинают проигрывать схеме.
Общий вывод я для себя сделал такой. Область применения схемы:
1. динамические запросы
2. точечные "инъекции" для изменения типовых запросов, если нет повышенных требований к производительности
3. моделирование сложных запросов в период разработки, а на прод помещать уже готовый текст запроса
Для отправки сообщения требуется регистрация/авторизация