В практике разработки на платформе 1С нередко возникает задача получения «крайних» записей из какой-либо таблицы. Это может быть необходимо для анализа временных рамок операций, сверки начальных и конечных остатков или формирования сложных аналитических отчетов. Рассмотрим ситуацию, когда нам требуется получить два самых первых (старых) документа и два самых последних (новых) документа ПриходныйКассовыйОрдер (ПКО) в рамках одного запроса.
Основная сложность заключается в том, что стандартная конструкция ПЕРВЫЕ в языке запросов 1С работает для всей выборки целиком. Мы не можем просто написать два выбора в одном пакете с разными направлениями сортировки УПОРЯДОЧИТЬ ПО, так как итоговая сортировка применяется к результату объединения. Если же мы попытаемся использовать ОБЪЕДИНИТЬ напрямую с конструкциями ПЕРВЫЕ и УПОРЯДОЧИТЬ, платформа выдаст ошибку, так как правила синтаксиса ограничивают использование упорядочивания внутри объединяемых частей запроса.
Проанализируем наиболее эффективный способ решения этой задачи, основанный на использовании временных таблиц.
Для того чтобы система корректно отобрала по две записи с каждой стороны списка, нам необходимо изолировать эти операции. Разберем по шагам, как это сделать с помощью механизма временных таблиц.
Шаг 1. Выбор первых записей. Сначала мы создаем запрос, который выбирает две самые ранние записи, и помещаем их во временную таблицу Таб1. Здесь важно использовать УПОРЯДОЧИТЬ ПО по возрастанию.
Шаг 2. Выбор последних записей. Затем мы создаем аналогичный запрос, но с сортировкой по убыванию (УБЫВ), чтобы получить «хвост» таблицы. Результат помещаем в таблицу Таб2.
Шаг 3. Объединение результатов. На финальном этапе мы выбираем данные из обеих временных таблиц и объединяем их.
Посмотрим на пример реализации кода:
ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 2
ПКО.Ссылка КАК Ссылка
ПОМЕСТИТЬ Таб1
ИЗ
Документ.ПКО КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.Ссылка;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 2
ПКО.Ссылка КАК Ссылка
ПОМЕСТИТЬ Таб2
ИЗ
Документ.ПКО КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.Ссылка УБЫВ;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Таб1.Ссылка КАК Ссылка
ИЗ
Таб1 КАК Таб1
ОБЪЕДИНИТЬ
ВЫБРАТЬ
Таб2.Ссылка
ИЗ
Таб2 КАК Таб2
Проанализируем ситуацию, которая часто встречается на небольших или тестовых базах. Что произойдет, если в справочнике или таблице документов всего 1, 2 или 3 записи? В этом случае одни и те же документы попадут как в первую временную таблицу, так и во вторую.
Если мы используем оператор ОБЪЕДИНИТЬ ВСЕ, система просто сложит результаты обеих выборок. В итоге мы получим дубликаты строк. Например, если в базе всего один документ, запрос с ОБЪЕДИНИТЬ ВСЕ вернет две строки с одной и той же ссылкой.
Чтобы избежать этой проблемы, следует использовать оператор ОБЪЕДИНИТЬ (без ключевого слова ВСЕ). Этот оператор выполняет сравнение строк и автоматически удаляет повторяющиеся записи. Таким образом, если документ является одновременно и «первым», и «последним» (в случае малого количества данных), в итоговой выборке он останется в единственном экземпляре.
В примерах выше мы использовали поле Ссылка для упорядочивания. Выясним причину, почему в реальных задачах это может быть не совсем корректно. Ссылка в 1С — это уникальный идентификатор (GUID). Хотя в современных версиях платформы GUID генерируются с учетом времени, они не гарантируют строгую хронологическую последовательность документов.
Если ваша задача — получить документы именно по времени их создания, лучше использовать поле Дата или, что еще более правильно, МоментВремени. Объект МоментВремени включает в себя и дату, и ссылку, что позволяет однозначно выстроить документы в порядке их записи в базу данных, даже если несколько документов созданы в одну и ту же секунду.
Рассмотрим пример с использованием МоментВремени:
ВЫБРАТЬ ПЕРВЫЕ 2
ПКО.Ссылка КАК Ссылка
ПОМЕСТИТЬ ПервыеЗаписи
ИЗ
Документ.ПКО КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.МоментВремени;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ ПЕРВЫЕ 2
ПКО.Ссылка КАК Ссылка
ПОМЕСТИТЬ ПоследниеЗаписи
ИЗ
Документ.ПКО КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.МоментВремени УБЫВ;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПервыеЗаписи.Ссылка
ИЗ
ПервыеЗаписи КАК ПервыеЗаписи
ОБЪЕДИНИТЬ
ВЫБРАТЬ
ПоследниеЗаписи.Ссылка
ИЗ
ПоследниеЗаписи КАК ПоследниеЗаписи
Иногда использование временных таблиц нежелательно (например, в некоторых динамических списках или при жестких ограничениях на создание таблиц в СУБД). В таком случае можно воспользоваться вложенными запросами. Суть остается той же: мы оборачиваем каждую выборку ПЕРВЫЕ 2 в отдельный подзапрос, а затем объединяем их.
Важно: При использовании вложенных запросов итоговая сортировка результата может быть произвольной. Если вам критично, чтобы сначала шли первые документы, а потом последние, стоит добавить техническое поле для сортировки.
Разберем этот пример:
ВЫБРАТЬ
Результат.Ссылка,
Результат.ПорядокСортировки
ИЗ
(ВЫБРАТЬ ПЕРВЫЕ 2
ПКО.Ссылка КАК Ссылка,
1 КАК ПорядокСортировки
ИЗ
Документ.ПКО КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.Дата
ОБЪЕДИНИТЬ
ВЫБРАТЬ ПЕРВЫЕ 2
ПКО.Ссылка,
2
ИЗ
Документ.ПКО КАК ПКО
УПОРЯДОЧИТЬ ПО
ПКО.Дата УБЫВ) КАК Результат
УПОРЯДОЧИТЬ ПО
Результат.ПорядокСортировки
В данном примере мы добавили поле ПорядокСортировки. Это гарантирует, что при выводе данных «старые» документы всегда будут находиться в начале таблицы, а «новые» — в конце, независимо от того, как СУБД решит оптимизировать план выполнения запроса.
При работе с большими объемами данных (например, если в таблице ПКО миллионы записей), следует учитывать следующие моменты:
Ссылка и Дата индексируются платформой автоматически. Сортировка по ним будет происходить достаточно быстро. Если же вы решите сортировать по какому-то произвольному реквизиту (например, СуммаДокумента), убедитесь, что для этого реквизита в конфигураторе установлено свойство "Индексировать".РАЗЛИЧНЫЕ вместе с ПЕРВЫЕ может потребовать дополнительных ресурсов процессора на стороне SQL-сервера. Если вы уверены, что ссылки уникальны (а для поля Ссылка это всегда так), ключевое слово РАЗЛИЧНЫЕ внутри временных таблиц можно опустить.ОБЪЕДИНИТЬ медленнее, чем ОБЪЕДИНИТЬ ВСЕ, так как требует от сервера БД выполнения операции сравнения всех строк. Если вы точно знаете, что выборки не пересекаются, используйте ОБЪЕДИНИТЬ ВСЕ для повышения скорости.Таким образом, мы проанализировали несколько способов решения задачи и выяснили, что использование временных таблиц с оператором ОБЪЕДИНИТЬ является наиболее сбалансированным и надежным методом получения первых и последних записей в одном запросе 1С.