Недавно понадобилось в запросе получить значение строковых кодов справочника без концевых пробелов. Как вы знаете, в языке запросов нет ничего похожего на функцию СокрЛП(), как же быть?
Можно, конечно, предварительно выполнить вспомогательный запрос, выгрузить его в таблицу значений, перебрать ее в цикле выполняя СокрЛП(), и затем передать обратно в нужный запрос в виде параметра (создать временную таблицу).
Или, если максимальная длина строки, как в случае кода справочника, фиксирована можно написать в запросе громоздкую конструкцию из вложенных конструкций ВЫБОР КОГДА...
Но это неинтересно. Благо на глаза попалась статья ildarovich-а //infostart.ru/public/90367/. Оказалось, что с помощью временой таблицы с числами 0,1,2,3... можно получить много нетривиальных вещей в запросах.
Далее несколько примеров.
Сначала удаление начальных и конечных пробелов, аналог функции СокрЛП(). Заодно мы попутно получаем длину обрезанных строк.
Идея очень простая: ищем первый пробельный символ после которого идет не пробел (учитываем, что первый же символ может быть не пробелом) и последний не пробел после которого идет пробел.
Дополнительно тут используется, что выражение ПОДСТРОКА(СтрокаСПробелами, ЗначениеБольшеДлиныСтроки, 1) возвращает пустую строку, а при сравнении cтандарт SQL говорит, что строки, отличающиеся незначащими символами в хвосте не различаются. В языке запросов 1С сравнение происходит точно так-же. Т.е. для запроса "" = " ".
// Вспомогательный порождающий запрос ВЫБРАТЬ 0 КАК Х ПОМЕСТИТЬ Регистр1 ОБЪЕДИНИТЬ ВЫБРАТЬ 1; ВЫБРАТЬ Младшие.Х + 2 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр2 ИЗ Регистр1 КАК Старшие, Регистр1 КАК Младшие; ВЫБРАТЬ Младшие.Х + 4 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр4 ИЗ Регистр2 КАК Старшие, Регистр2 КАК Младшие; ВЫБРАТЬ Младшие.Х + 16 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр8 ИЗ Регистр4 КАК Старшие, Регистр4 КАК Младшие; ВЫБРАТЬ Младшие.Х + 256 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр16 ИЗ Регистр8 КАК Старшие, Регистр8 КАК Младшие; // Создание таблицы с исходными строками, для тестирования ВЫБРАТЬ "1 " КАК СтрокаСПробелами ПОМЕСТИТЬ ИсходнаяТаблица ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "22 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "333 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ " 4 4 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "5 555 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ " 666666 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "7777777 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ " 88888888 " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "999999999"; // Непосредственно запрос обрезающий начальные и конечные пробелы и попутно получающий реальную длину строки ВЫБРАТЬ СтрокаСПробелами КАК СтрокаСПробелами, МАКСИМУМ(Цикл_2.Х + 1) КАК ПоследнийЗначащийСимвол, МИНИМУМ(Цикл_1.Х + 1) КАК ПервыйЗначащийСимвол ПОМЕСТИТЬ ПромежуточнаяТаблица ИЗ ИсходнаяТаблица ЛЕВОЕ СОЕДИНЕНИЕ Регистр16 КАК Цикл_1 ПО (ПОДСТРОКА(СтрокаСПробелами, Цикл_1.Х, 1) = " " И ПОДСТРОКА(СтрокаСПробелами, Цикл_1.Х + 1, 1) <> " ") ИЛИ (Цикл_1.Х = 0 И ПОДСТРОКА(СтрокаСПробелами, Цикл_1.Х + 1, 1) <> " ") ЛЕВОЕ СОЕДИНЕНИЕ Регистр16 КАК Цикл_2 ПО ПОДСТРОКА(СтрокаСПробелами, Цикл_2.Х + 1, 1) <> " " И ПОДСТРОКА(СтрокаСПробелами, Цикл_2.Х + 2, 1) = " " СГРУППИРОВАТЬ ПО СтрокаСПробелами ; ВЫБРАТЬ ПОДСТРОКА(СтрокаСПробелами, ПервыйЗначащийСимвол, ПоследнийЗначащийСимвол - ПервыйЗначащийСимвол + 1) КАК СтрокаОбрезанная, ПоследнийЗначащийСимвол - ПервыйЗначащийСимвол + 1 КАК ДлинаСтроки ИЗ ПромежуточнаяТаблица
Теперь выполним разложение строки на слова, используя эту же идею: не пробельный символ, перед которым идет пробел, считаем началом слова, и к нему берем первый не пробельный символ, после которого идет пробел.
Здесь в качестве "пробельных" символов введена вспомогательная таблица с несколькими разделителями. Т.е. мы выполняем разложение строки не по пробелам, а по заданным разделителям.
// Вспомогательный порождающий запрос ВЫБРАТЬ 0 КАК Х ПОМЕСТИТЬ Регистр1 ОБЪЕДИНИТЬ ВЫБРАТЬ 1; ВЫБРАТЬ Младшие.Х + 2 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр2 ИЗ Регистр1 КАК Старшие, Регистр1 КАК Младшие; ВЫБРАТЬ Младшие.Х + 4 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр4 ИЗ Регистр2 КАК Старшие, Регистр2 КАК Младшие; ВЫБРАТЬ Младшие.Х + 16 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр8 ИЗ Регистр4 КАК Старшие, Регистр4 КАК Младшие; ВЫБРАТЬ Младшие.Х + 256 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр16 ИЗ Регистр8 КАК Старшие, Регистр8 КАК Младшие; // Таблица разделителей слов ВЫБРАТЬ " " КАК Разделитель ПОМЕСТИТЬ Разделители ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ " " ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ ";" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ ","; // Создание таблицы с исходной строкой для разложения ВЫБРАТЬ "Съешь еще этих французских булок" КАК СтрокаДляРазложения ПОМЕСТИТЬ ИсходнаяТаблица; // Получаем границы слов ВЫБРАТЬ Цикл_1.Х + 1 КАК НачалоСлова, МИНИМУМ(Цикл_2.Х + 1) КАК КонецСлова ПОМЕСТИТЬ ГраницыСлов ИЗ ИсходнаяТаблица ЛЕВОЕ СОЕДИНЕНИЕ Регистр16 КАК Цикл_1 ПО (ПОДСТРОКА(СтрокаДляРазложения, Цикл_1.Х, 1) В (ВЫБРАТЬ Разделитель ИЗ Разделители) ИЛИ Цикл_1.Х = 0) И ПОДСТРОКА(СтрокаДляРазложения, Цикл_1.Х + 1, 1) НЕ В (ВЫБРАТЬ Разделитель ИЗ Разделители) ЛЕВОЕ СОЕДИНЕНИЕ Регистр16 КАК Цикл_2 ПО ПОДСТРОКА(СтрокаДляРазложения, Цикл_2.Х + 1, 1) НЕ В (ВЫБРАТЬ Разделитель ИЗ Разделители) И ПОДСТРОКА(СтрокаДляРазложения, Цикл_2.Х + 2, 1) В (ВЫБРАТЬ Разделитель ИЗ Разделители) И Цикл_2.Х >= Цикл_1.Х СГРУППИРОВАТЬ ПО Цикл_1.Х ; // Исходя из полученных границ слов выполняем разложение строки ВЫБРАТЬ ПОДСТРОКА(СтрокаДляРазложения, НачалоСлова, КонецСлова - НачалоСлова + 1) ИЗ ИсходнаяТаблица, ГраницыСлов
Решето Эратосфена, выдающее таблицу простых чисел
// Вспомогательный порождающий запрос ВЫБРАТЬ 0 КАК Х ПОМЕСТИТЬ Регистр1 ОБЪЕДИНИТЬ ВЫБРАТЬ 1; ВЫБРАТЬ Младшие.Х + 2 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр2 ИЗ Регистр1 КАК Старшие, Регистр1 КАК Младшие; ВЫБРАТЬ Младшие.Х + 4 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр4 ИЗ Регистр2 КАК Старшие, Регистр2 КАК Младшие; ВЫБРАТЬ Младшие.Х + 16 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр8 ИЗ Регистр4 КАК Старшие, Регистр4 КАК Младшие; ВЫБРАТЬ Младшие.Х + 256 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр16 ИЗ Регистр8 КАК Старшие, Регистр8 КАК Младшие; // Решето Эратосфена, на выходе получаем таблицу всех простых чисел меньших 65536 + 2 ВЫБРАТЬ Регистр4.Х + 2 КАК Х ПОМЕСТИТЬ Решето4 ИЗ Регистр4 ЛЕВОЕ СОЕДИНЕНИЕ Регистр2 ПО Регистр4.Х > Регистр2.Х + 1 И (Регистр4.Х + 2) / (Регистр2.Х + 2) = ВЫРАЗИТЬ((Регистр4.Х + 2) / (Регистр2.Х + 2) КАК Число(32)) ГДЕ Регистр2.Х ЕСТЬ NULL ; ВЫБРАТЬ Регистр8.Х + 2 КАК Х ПОМЕСТИТЬ Решето8 ИЗ Регистр8 ЛЕВОЕ СОЕДИНЕНИЕ Решето4 ПО Регистр8.Х + 1 > Решето4.Х И (Регистр8.Х + 2) / Решето4.Х = ВЫРАЗИТЬ((Регистр8.Х + 2) / Решето4.Х КАК Число(32)) ГДЕ Решето4.Х ЕСТЬ NULL ; ВЫБРАТЬ Регистр16.Х + 2 ПростоеЧисло ИЗ Регистр16 ЛЕВОЕ СОЕДИНЕНИЕ Решето8 ПО Регистр16.Х + 1 > Решето8.Х И (Регистр16.Х + 2) / Решето8.Х = ВЫРАЗИТЬ((Регистр16.Х + 2) / Решето8.Х КАК Число(32)) ГДЕ Решето8.Х ЕСТЬ NULL
Степени двойки до 2^106, большие степени не помещаются в 32 знака (не разряда!) числа, которыми оперирует 1С
ВЫБРАТЬ 0 КАК Степень, 1 КАК Результат ПОМЕСТИТЬ Регистр1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1, 2; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ПОМЕСТИТЬ Регистр2 ИЗ Регистр1 КАК ПервыйСомножитель, Регистр1 КАК ВторойСомножитель; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ПОМЕСТИТЬ Регистр4 ИЗ Регистр2 КАК ПервыйСомножитель, Регистр2 КАК ВторойСомножитель; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ПОМЕСТИТЬ Регистр8 ИЗ Регистр4 КАК ПервыйСомножитель, Регистр4 КАК ВторойСомножитель; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ПОМЕСТИТЬ Регистр16 ИЗ Регистр8 КАК ПервыйСомножитель, Регистр8 КАК ВторойСомножитель; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ПОМЕСТИТЬ Регистр32 ИЗ Регистр16 КАК ПервыйСомножитель, Регистр16 КАК ВторойСомножитель; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ПОМЕСТИТЬ Регистр64 ИЗ Регистр32 КАК ПервыйСомножитель, Регистр32 КАК ВторойСомножитель; ВЫБРАТЬ РАЗЛИЧНЫЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень КАК Степень, ПервыйСомножитель.Результат * ВторойСомножитель.Результат КАК Результат ИЗ Регистр64 КАК ПервыйСомножитель, Регистр64 КАК ВторойСомножитель ГДЕ ПервыйСомножитель.Степень + ВторойСомножитель.Степень < 107
Запрос, выполняющий точное вычисление квадратного корня числа в диапазоне 0 - 1 048 576, развитие запроса из статьи ildarovich-а (см. ссылку в начале), который вычислял целое значение квадратного корня.
// Вспомогательный порождающий запрос ВЫБРАТЬ 0 КАК Х ПОМЕСТИТЬ Регистр1 ОБЪЕДИНИТЬ ВЫБРАТЬ 1; ВЫБРАТЬ Младшие.Х + 2 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр2 ИЗ Регистр1 КАК Старшие, Регистр1 КАК Младшие; ВЫБРАТЬ Младшие.Х + 4 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр4 ИЗ Регистр2 КАК Старшие, Регистр2 КАК Младшие; ВЫБРАТЬ Младшие.Х + 16 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр8 ИЗ Регистр4 КАК Старшие, Регистр4 КАК Младшие; ВЫБРАТЬ Младшие.Х + 256 * Старшие.Х КАК Х ПОМЕСТИТЬ Регистр16 ИЗ Регистр8 КАК Старшие, Регистр8 КАК Младшие; // Для проверки создается таблица с различными значениями аргумента ВЫБРАТЬ 123456 КАК У ПОМЕСТИТЬ Аргументы ОБЪЕДИНИТЬ ВЫБРАТЬ 555555 ОБЪЕДИНИТЬ ВЫБРАТЬ 987654; // Вычисляем сначала целую часть (решение ildarovich-а) ВЫБРАТЬ У КАК У, МАКСИМУМ(Х) КАК Квадратный_корень_У, У - МАКСИМУМ(Х) * МАКСИМУМ(Х) КАК Дельта ПОМЕСТИТЬ ЦелаяЧастьКорня ИЗ Аргументы ЛЕВОЕ СОЕДИНЕНИЕ Регистр16 ПО (Х * Х <= У) СГРУППИРОВАТЬ ПО У; // Продолжение запроса, вычисляющее дополнительные знаки после запятой // Вспомогательная таблица для уменьшения количества делений ВЫБРАТЬ Х КАК Х, Х/65536 КАК Дробь ПОМЕСТИТЬ Дроби16 ИЗ Регистр16; // Теперь уточним значение с точностью до 1/65536 ВЫБРАТЬ У КАК У, Квадратный_корень_У + МАКСИМУМ(Дробь) КАК Квадратный_корень_У, У - (Квадратный_корень_У + МАКСИМУМ(Дробь)) * (Квадратный_корень_У + МАКСИМУМ(Дробь)) КАК Дельта ПОМЕСТИТЬ Приближение1 ИЗ ЦелаяЧастьКорня ЛЕВОЕ СОЕДИНЕНИЕ Дроби16 ПО ((Квадратный_корень_У + Дробь) * (Квадратный_корень_У + Дробь) <= У) СГРУППИРОВАТЬ ПО У, Квадратный_корень_У; // Вспомогательная таблица для уменьшения количества делений ВЫБРАТЬ Х КАК Х, Дробь/65536 КАК Дробь ПОМЕСТИТЬ Дроби32 ИЗ Дроби16; // С точностью до 1/4294967296 ВЫБРАТЬ У КАК У, Квадратный_корень_У + МАКСИМУМ(Дробь) КАК Квадратный_корень_У, У - (Квадратный_корень_У + МАКСИМУМ(Дробь)) * (Квадратный_корень_У + МАКСИМУМ(Дробь)) КАК Дельта ПОМЕСТИТЬ Приближение2 ИЗ Приближение1 ЛЕВОЕ СОЕДИНЕНИЕ Дроби32 ПО ((Квадратный_корень_У + Дробь) * (Квадратный_корень_У + Дробь) <= У) СГРУППИРОВАТЬ ПО У, Квадратный_корень_У; // Вспомогательная таблица для уменьшения количества делений ВЫБРАТЬ Х КАК Х, Дробь/65536 КАК Дробь ПОМЕСТИТЬ Дроби48 ИЗ Дроби32; // С точностью до 1/281474976710656 ВЫБРАТЬ У КАК У, Квадратный_корень_У + МАКСИМУМ(Дробь) КАК Квадратный_корень_У, У - (Квадратный_корень_У + МАКСИМУМ(Дробь)) * (Квадратный_корень_У + МАКСИМУМ(Дробь)) КАК Дельта ПОМЕСТИТЬ Приближение3 ИЗ Приближение2 ЛЕВОЕ СОЕДИНЕНИЕ Дроби48 ПО ((Квадратный_корень_У + Дробь) * (Квадратный_корень_У + Дробь) <= У) СГРУППИРОВАТЬ ПО У, Квадратный_корень_У; // Вспомогательная таблица для уменьшения количества делений ВЫБРАТЬ Х КАК Х, Дробь/65536 КАК Дробь ПОМЕСТИТЬ Дроби64 ИЗ Дроби48; // С точностью до 1/18446744073709551616 ВЫБРАТЬ У КАК У, Квадратный_корень_У + МАКСИМУМ(Дробь) КАК Квадратный_корень_У, У - (Квадратный_корень_У + МАКСИМУМ(Дробь)) * (Квадратный_корень_У + МАКСИМУМ(Дробь)) КАК Дельта ИЗ Приближение3 ЛЕВОЕ СОЕДИНЕНИЕ Дроби64 ПО ((Квадратный_корень_У + Дробь) * (Квадратный_корень_У + Дробь) <= У) СГРУППИРОВАТЬ ПО У, Квадратный_корень_У