Как в запросе 1С сгенерировать последовательность чисел и вычислить их квадраты?

Программист IT и автоматизация бизнеса
← На главную

Рассмотрим задачу, которая часто встречается в учебных целях или при решении нестандартных задач: необходимо с помощью одного запроса 1С получить таблицу с числовой последовательностью, которой нет в базе данных. В нашем примере требуется сгенерировать последовательность квадратов чисел в заданном диапазоне. Например, для диапазона от 2 до 5 результат должен быть таблицей со значениями 4, 9, 16, 25.

Прежде чем перейти к решению, важно уточнить терминологию. В исходном вопросе такая последовательность была названа "геометрической прогрессией", что не совсем верно. Геометрическая прогрессия — это ряд, где каждый следующий член получается умножением предыдущего на постоянный знаменатель (например, 2, 4, 8, 16). В нашей задаче мы имеем дело с последовательностью квадратов целых чисел (2², 3², 4², 5²).

Для создания таких последовательностей "из воздуха", без обращения к существующим таблицам данных, в 1С используется мощный механизм — порождающие (генерирующие) запросы. Разберем два способа их построения.

Решение 1: Генерация последовательности через двоичное разложение

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

  1. Создание базовой таблицы. Сначала создается простейшая таблица с двумя записями — 0 и 1. Это основа для всех дальнейших вычислений.
  2. Расширение таблицы. Далее эта таблица соединяется сама с собой. Каждое такое соединение удваивает количество строк и позволяет сгенерировать следующий разряд числа. Например, соединив таблицу с числами {0, 1} саму с собой, мы можем получить уже 4 числа: 0, 1, 2, 3.
  3. Генерация полного ряда. Повторяя этот процесс несколько раз, мы можем получить достаточно длинную последовательность целых чисел. Например, 5 таких соединений дадут нам последовательность от 0 до 65535.
  4. Финальный расчет. В последней части запроса мы выбираем числа из сгенерированной последовательности, фильтруем их по заданному диапазону с помощью параметров &мин и &макс, и для каждого числа вычисляем его квадрат.

Посмотрим на итоговый код запроса:


ВЫБРАТЬ 0 КАК Х
ПОМЕСТИТЬ Регистр1
ОБЪЕДИНИТЬ
ВЫБРАТЬ 1
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ Младшие.Х + 2 * Старшие.Х КАК Х
ПОМЕСТИТЬ Регистр2
ИЗ Регистр1 КАК Младшие, Регистр1 КАК Старшие
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ Младшие.Х + 4 * Старшие.Х КАК Х
ПОМЕСТИТЬ Регистр4
ИЗ Регистр2 КАК Младшие, Регистр2 КАК Старшие
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ Младшие.Х + 16 * Старшие.Х КАК Х
ПОМЕСТИТЬ Регистр8
ИЗ Регистр4 КАК Младшие, Региstr4 КАК Старшие
;
///////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ Младшие.Х + 256 * Старшие.Х КАК Х
ПОМЕСТИТЬ целые
ИЗ Регистр8 КАК Младшие, Регистр8 КАК Старшие
;
/////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    целые.Х * целые.Х КАК КвадратЧисла
ИЗ
    целые
ГДЕ
    целые.Х МЕЖДУ &мин И &макс
УПОРЯДОЧИТЬ ПО
    КвадратЧисла

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

Решение 2: Генерация через декартово произведение и функцию АВТОНОМЕРЗАПИСИ()

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

Разберем шаги:

  1. Создание "множителей". Внутри запроса с помощью конструкции ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ... создаются вложенные подзапросы. Каждый из них возвращает таблицу, например, из 10 строк.
  2. Декартово произведение. Эти таблицы соединяются друг с другом через СОЕДИНЕНИЕ ... ПО ИСТИНА. Если соединить две таблицы по 10 строк, получится 100 строк. Три таблицы — 1000 строк, и так далее. Шесть таких таблиц дадут нам 1,000,000 строк, чего обычно достаточно для большинства задач.
  3. Ограничение и нумерация. С помощью конструкции ВЫБРАТЬ ПЕРВЫЕ N мы сразу ограничиваем количество генерируемых строк до необходимого нам. Затем функция АВТОНОМЕРЗАПИСИ() нумерует полученные строки, создавая последовательность от 1 до N. Важно: АВТОНОМЕРЗАПИСИ() работает только при помещении результата во временную таблицу.
  4. Сдвиг диапазона и вычисление. В финальном запросе мы берем полученный номер строки (Ключ), сдвигаем его, чтобы он начинался с нужного нам значения (Т.Ключ + &МинЗначение - 1), и вычисляем квадрат этого числа.

Ниже представлен код, который генерирует последовательность и сразу вычисляет квадраты. Обратите внимание, что текст запроса нужно формировать программно, чтобы подставить нужное количество строк в конструкцию ПЕРВЫЕ.


// Пример программного формирования и выполнения запроса

МинЗначение = 2;
МаксЗначение = 5;

ТекстЗапроса = "
|ВЫБРАТЬ ПЕРВЫЕ " + (МаксЗначение - МинЗначение + 1) + "
|    АВТОНОМЕРЗАПИСИ() КАК Ключ
|ПОМЕСТИТЬ ПоследовательностьЧисел
|ИЗ
|    (ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1) КАК Т1
|СОЕДИНЕНИЕ
|    (ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1) КАК Т2 ПО ИСТИНА
|СОЕДИНЕНИЕ
|    (ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1) КАК Т3 ПО ИСТИНА
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    (ПоследовательностьЧисел.Ключ + &МинЗначение - 1) * (ПоследовательностьЧисел.Ключ + &МинЗначение - 1) КАК КвадратЧисла
|ИЗ
|    ПоследовательностьЧисел КАК ПоследовательностьЧисел";

Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("МинЗначение", МинЗначение);

Результат = Запрос.Выполнить().Выгрузить();
Результат.ВыбратьСтроку();

Этот метод очень гибок, так как позволяет легко наращивать максимальный размер последовательности, просто добавляя копированием новые блоки СОЕДИНЕНИЕ. При этом за счет конструкции ПЕРВЫЕ СУБД выполнит только необходимое количество соединений, что делает запрос достаточно производительным.

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

← На главную