1
0
mirror of https://github.com/ones-devguide/dev-rules.git synced 2026-04-24 03:33:20 +02:00
Files
dev-rules/docs/theory/request.md
T
2025-12-19 20:57:49 +07:00

18 KiB

sidebar_position, sidebar_label, title
sidebar_position sidebar_label title
7 Запросы Запросы

Оптимизация запросов

Общие принципы

  • Выбирайте только нужные поля, избегайте ВЫБРАТЬ *.
  • Переносите вычисления (свертку, сортировку) в СУБД, а не делайте их на стороне 1С.
  • Не усложняйте запросы без необходимости. Слишком сложные запросы (особенно с подзапросами и множеством таблиц) могут привести к неоптимальным планам выполнения от СУБД.
  • Не пытайтесь любой ценой перенести всю логику в СУБД. Иногда проще постобработать данные на стороне 1С или использовать несколько простых запросов вместо одного сложного.

Индексы и условия отбора

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

  • Автоматические индексы создаются платформой (по ссылке, регистратору, периоду+измерениям и т.д).
  • Дополнительные индексы создавайте осознанно, только для частых и критичных запросов.
  • Не индексируйте низкоселективные поля (например, Булево, если нет сильного перекоса в значениях).

Пример проблемы: В регистре измерения [Склад, Номенклатура, Качество]. Запрос с отбором по Номенклатура, но без Склад не сможет использовать индекс эффективно, так как нарушено требование "поля подряд".

Решение: Создать дополнительный индекс или (осторожно) изменить порядок измерений.

Работа с составными типами (ссылочные поля)

Главная проблема: Обращение к полю через точку (Документ.Регистратор.Дата) для поля составного типа приводит к неявному соединению со ВСЕМИ таблицами возможных типов (например, со всеми видами документов). Это резко замедляет запрос.

Решение:

  1. Избегайте таких обращений в условиях отбора и выборки.
  2. Используйте функцию ВЫРАЗИТЬ(), если заранее известен конкретный тип.
  3. Дублируйте данные (например, добавьте в регистр реквизит ДатаРегистратора).
  4. Уточняйте составной тип в метаданных, не используйте "любаяСсылка" без необходимости.

Временные таблицы и подзапросы

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

Пример: Получение "последней цены" через ПО ... В (ВЫБРАТЬ МАКСИМУМ(Период) ...) лучше делать в два шага через временную таблицу.

Виртуальные таблицы (остатки, срезы)

  1. Всегда передавайте условия отбора в ПАРАМЕТРЫ виртуальной таблицы, а не в ГДЕ основного запроса.
    • ...Остатки(, Склад = &Склад) (ПРАВИЛЬНО)
    • ...Остатки() ГДЕ Склад = &Склад (НЕПРАВИЛЬНО)
  2. В параметрах виртуальных таблиц используйте только простые условия (Измерение = Значение). Избегайте подзапросов и обращений через точку.
  3. Для остатков на текущую дату не указывайте дату в параметрах таблицы Остатки().
  4. Чтобы получить остатки эффективно, используйте все измерения регистра в основном запросе (в ВЫБРАТЬ или в ПО соединения).

Эффективные условия в запросах

  • Основное условие (которое сильно сокращает выборку) должно использовать индекс и содержать только операции, по которым возможен поиск по индексу: =, В, >, <, ПОДОБНО "Нач%", МЕЖДУ.
  • Оператор ИЛИ в основном условии допустим только для последнего поля индекса и если его можно заменить на В.
  • Функции (ПОДСТРОКА(), МЕСЯЦ()) и арифметические операции в условиях не позволяют использовать индекс. Переписывайте:
    • ПОДСТРОКА(Поле,1,3) = "АБВ" -> Поле ПОДОБНО "АБВ%"
    • МЕСЯЦ(Дата) = 1 -> Дата МЕЖДУ ДАТАВРЕМЯ(2023,01,01) И ДАТАВРЕМЯ(2023,01,31,23,59,59)
  • Конструкцию ВЫБОР используйте только в дополнительных условиях в секции ГДЕ.

Временные таблицы (рекомендации по использованию)

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

Прочие важные советы

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

Ответы на ключевые вопросы по запросам

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

Ответ: Использовать типизацию через ВЫРАЗИТЬ. Дополнительно ограничить в условие ГДЕ Регистратор ССЫЛКА Документ.ИмяДокумента

Отличия полного соединения от внутреннего?

Ответ:

  • Внутреннее: Возвращает только совпадающие строки из обеих таблиц
  • Полное: Возвращает все строки из обеих таблиц (NULL при отсутствии совпадений)

Что оптимальнее: обход выборки или выгрузка в ТЗ?

Ответ: Обход выборки. Таблица значений хранится в оперативной памяти → при больших объемах можно получить ошибку "Недостаточно памяти".

Для чего нужна конструкция РАЗРЕШЕННЫЕ?

Ответ: Позволяет исключить записи, к которым у пользователя нет прав доступа (работает с RLS).

Можно ли использовать запросы в цикле?

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

Как пронумеровать строки в выборке?

Ответ: Использовать АВТОНОМЕРЗАПИСИ:

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

Ответ: Вызвать Выборка.Сбросить() для сброса указателя на начало.

Для чего индексировать временные таблицы?

Ответ: Для ускорения соединений с большими ВТ. Оправдано, если:

  • ВТ очень большая
  • Используется в множественных соединениях
  • Передается через МенеджерВременныхТаблиц

Почему Регистратор.Дата неоптимально?

Ответ: Поле Регистратор составного типа → неявное соединение со всеми таблицами возможных типов. Решение: Вынести дату в отдельный реквизит или использовать ВЫРАЗИТЬ.

Где можно накладывать условия отбора?

Ответ: В ГДЕ, условиях соединения (ПО), параметрах виртуальных таблиц, ИМЕЮЩИЕ.

Как обойти выборку с условием "Сумма = 5000"?

Ответ: Использовать Выборка.НайтиСледующий() с отбором по условию.

Три основные ошибки в запросах, влияющие на производительность:

Ответ:

  1. Обращение через точку к полям составного типа
  2. Использование ИЛИ в основном условии отбора
  3. Соединение с подзапросами вместо временных таблиц

Как поместить таблицу значений в запрос?

Ответ:

  1. Описать ТЗ в запросе с типизацией полей
  2. Передать как параметр через ПОМЕСТИТЬ

Как оптимально получить склад из регистратора-документа?

Ответ:

ВЫБРАТЬ 
    ВЫРАЗИТЬ(Регистратор КАК Документ.ПоступлениеТоваров).Склад
ИЗ РегистрНакопления
ГДЕ Регистратор ССЫЛКА Документ.ПоступлениеТоваров

Принцип получения курсов валют на каждый день:

Ответ:

  1. Временная таблица для отбора дат (производственный календарь в типовых)
  2. Соединяем с этой таблицей физичесткую таблицу курса валют. Соединение ДатаМесяц >= КурсПериод. Максимум КурсПериод.
  3. Вторую временную таблицу соединяем с физической таблицей валют внутренним соединеием по валюте и периоду

Где хранится временная таблица?

Ответ: В tempdb СУБД.

Как получить первую запись в регистре?

Ответ: ВЫБРАТЬ ПЕРВЫЕ 1

Как получить документы РТУ с >5 номенклатур?

Ответ: ИМЕЮЩИЕ КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура) > 5

Минус обращения через точку к составному типу:

Ответ: Соединение со всеми таблицами возможных типов → резкое падение производительности.

На какую дату остатки, если не указать дату?

Ответ: Актуальные остатки (на 01.01.3999).

Разница между ГДЕ и ИМЕЮЩИЕ:

Ответ:

  • ГДЕ — фильтрация до группировки (отбор строк)
  • ИМЕЮЩИЕ — фильтрация после группировки (отбор групп)

Разница ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ:

Ответ:

  • ОБЪЕДИНИТЬудаляет дубли автоматически
  • ОБЪЕДИНИТЬ ВСЕ — сохраняет все строки, включая дубли

Почему не выгружать в ТЗ для обхода?

Ответ: ТЗ хранится в оперативной памяти → риск исчерпания памяти при больших объемах данных.

Какой SQL запрос строится при обращении к таблице остатков?

Ответ: Сложный запрос с использованием итоговых таблиц и досчетом по движениям, если период не совпадает с итогами.

Что лучше: дата документа или момент времени?

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

Как изменить запрос с "ИЛИ" и зачем?

Ответ: Заменить на ОБЪЕДИНИТЬ ВСЕ или МЕЖДУ, если возможно. ИЛИ часто мешает использованию индексов.

Почему не смешивать составные и простые типы?

Ответ: Для каждого составного типа создаются отдельные колонки в БД → сложная структура, трудности с индексами и отборами.

Как получить изменения объектов на узле плана обмена?

Ответ: Выбирать таблицу Изменения: ИмяОсновнойТаблицы.Изменения

ВЫБРАТЬ
	НоменклатураИзменения.НомерСообщения КАК НомерСообщения,
	НоменклатураИзменения.Ссылка КАК Ссылка
ИЗ
	Справочник.Номенклатура.Изменения КАК НоменклатураИзменения
ГДЕ
	НоменклатураИзменения.Узел = &Узел

Что будет при МАКСИМУМ(МоментВремени)?

Ответ: Ошибка "Неправильные параметры", т.к. МоментВремени — виртуальное поле, вычисляемое при выполнении.

Почему Выборка.Ссылка.Наименование неоптимально?

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

Когда не удалять временные таблицы?

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

📚 Материалы