Условие задачи
На языке запросов 1С необходимо с помощью языка запросов выбрать M записей начиная с записи N в справочнике или документе.
Решение 1. Вариант "НЕ В"
Подобная задача часто используется, например, для пагинации выборки данных. Т.е. чтобы все записи выборки из результата выполнения запроса выбрать не сразу, а получать равными частями, чтобы было удобно их постранично выводить.
В MS SQL есть операторы TOP (ПЕРВЫЕ) и SKIP (ПРОПУСТИТЬ), которые позволяют это сделать. В запросах на платформе 1С нет оператора SKIP.
Подумаем, что можно с этим сделать.
Возьмем типовую 1С:Управление торговлей 11 и постараемся промоделировать ситуацию на реальной задаче.
Для справочника Номенклатура необходимо написать запрос, который выведет все элементы исключая группы с N = 10 и M = 10 с сортировкой по наименованию. Т.е. вывести элементы с десятого по девятнадцатый.
Мы имеем возможность в 1С использовать только получение первых записей. Поэтому идея решения на SQL следующая:
SELECT TOP N+M * FROM TABLE ORDER BY n EXCEPT SELECT TOP N * FROM TABLE ORDER BY n
Получим первые N+M элементов, и исключим из выборки первые N, в результате у нас останется искомое M элементов начиная с N. Схематично:
Выбираем сначала от 0 до N+M и исключаем от 0 до N. А теперь переиначим на языке 1С. В результате получим вот такой запрос:
ВЫБРАТЬ ПЕРВЫЕ 10 // M Номенклатура.Ссылка КАК Ссылка ИЗ Справочник.Номенклатура КАК Номенклатура ГДЕ НЕ Номенклатура.Ссылка В (ВЫБРАТЬ ПЕРВЫЕ 10 // N Номенклатура.Ссылка КАК Ссылка ИЗ Справочник.Номенклатура КАК Номенклатура ГДЕ Номенклатура.ЭтоГруппа = ЛОЖЬ УПОРЯДОЧИТЬ ПО Номенклатура.Наименование, Ссылка) И Номенклатура.ЭтоГруппа = ЛОЖЬ УПОРЯДОЧИТЬ ПО Наименование, Ссылка
Проверяем в справочнике Номенклатура:
Получаем искомый результат. То, что и хотели получить. Единственный нюанс - это рекомендации фирмы 1С, что нежелательно использовать конструкцию "НЕ В (...)" (как и В (...)). Будет полное сканирование таблиц, что не очень хорошо отразится на производительности.
Важно! Выборки (то что в условии НЕ В ... и в главном запросе) должны быть с одинаковым упорядочиванием (у нас по наименованию и далее по ссылке) и упорядочивание должно быть всегда по какому-либо полю. Иначе запросы могут отработать не верно и вернуть не те результаты. Если поля упорядочивания нет, то можно использовать поле "Ссылка".
Решение 2. Вариант "Временная таблица"
Давайте попробуем с другой стороны подойти к решению. В первом решении у нас есть проблема с тем, что есть две выборки, каждая из которых выполняется отдельно, а потом из одной вычитается другая. Обойти это можно, если мы внутреннюю выборку вынесем во временную таблицу и соединим их так, чтобы из внешней выборки была исключена другая выборка.
ВЫБРАТЬ ПЕРВЫЕ 10 // N Номенклатура.Ссылка КАК Ссылка ПОМЕСТИТЬ ВТ_1 ИЗ Справочник.Номенклатура КАК Номенклатура ГДЕ Номенклатура.ЭтоГруппа = ЛОЖЬ УПОРЯДОЧИТЬ ПО Номенклатура.Наименование, Ссылка ИНДЕКСИРОВАТЬ ПО Ссылка ; //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ПЕРВЫЕ 10 // M Номенклатура.Ссылка КАК Ссылка ИЗ Справочник.Номенклатура КАК Номенклатура ЛЕВОЕ СОЕДИНЕНИЕ ВТ_1 КАК ВТ_1 ПО (Номенклатура.Ссылка = ВТ_1.Ссылка) ГДЕ ВТ_1.Ссылка ЕСТЬ NULL И Номенклатура.ЭтоГруппа = ЛОЖЬ УПОРЯДОЧИТЬ ПО Номенклатура.Наименование, Ссылка
После выполнения получаем ровно тот же самый ответ. Тратится больше времени на создание временной таблицы ВТ_1, но при этом быстрее исключаются данные из второго запроса за счет использования ЕСТЬ NULL. Это будет работать быстрее.
Но и тут есть свои нюансы. Мы тратим время на создание временной таблицы. Это будет иметь смысл, если данных много, но если их мало, изначально, в справочнике Номенклатура, то решение 1, будет работать быстрее. Тоже вопрос, надо ли индексировать ВТ_1 по ссылке? Если данных мало, то смысла в этой операции скорее нет, чем есть, но все резко меняется, если данных становится гораздо больше.
Выводы
Решение 1, в целом, способ достаточно хороший и может быть использован в реальной работе. Самое главное, что он работает и позволяет обходить ограничение на использование оператора SKIP в запросах, но с нюансами по производительности.
Решение 2 создает временную таблицу, но быстрее исключает данные при исключении. Суммарно эти две операции надо считать вместе (создание ВТ + выборка с исключением). Да и в целом этот способ будет наверное изначально предпочтительней.