Пример оптимизации запроса
Загрузка номенклатуры в 1С:Розницу из другой базы (не 1С) с использованием веб-сервиса. При обычных условиях, все работало. Однако время обработки необъяснимо нелинейно увеличивалось при росте объема передаваемых данных. Разработали это задолго до моего прихода, кто - неизвестно...
Стал разбираться. Узкое место - некий запрос, связывающий таблицу поступающих данных с имеющейся номенклатурой. Надо сказать, что я такой себе оптимизатор. Никогда в жизни не анализировал планы запросов. Но с другой стороны часто бывает так, что это и не требуется. Посмотрите внимательно на это произведение...
Запрос = Новый Запрос(); Запрос.МенеджерВременныхТаблиц = мМенеджерВременныхТаблиц; Запрос.Текст = "ВЫБРАТЬ | ТаблицаТоваров.Артикул КАК Артикул, | ТаблицаТоваров.Наименование, | ТаблицаТоваров.Размер, | ТаблицаТоваров.СтавкаНДС, | ТаблицаТоваров.УчетПоХарактеристикам, | ТаблицаТоваров.Штрихкод, | ТаблицаТоваров.Удалено, | ТаблицаТоваров.КодТовара КАК КодТовара, | ТаблицаТоваров.ПодарочныйСертификат, | ТаблицаТоваров.ОсобенностиУчетаНоменклатуры | |ПОМЕСТИТЬ ТаблицаТоваров | |ИЗ | &ТаблицаТоваров КАК ТаблицаТоваров | |ИНДЕКСИРОВАТЬ ПО | КодТовара"; Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаДанных); Запрос.Выполнить(); Запрос = Новый Запрос(); Запрос.МенеджерВременныхТаблиц = мМенеджерВременныхТаблиц; Запрос.Текст = "ВЫБРАТЬ | ТаблицаТоваров.Артикул КАК Артикул, | ТаблицаТоваров.Наименование КАК Наименование, | НоменклатураДополнительныеРеквизиты.Ссылка КАК Номенклатура, | ВЫБОР ТаблицаТоваров.СтавкаНДС | КОГДА ""10%"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС10) | КОГДА ""Без НДС"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.БезНДС) | КОГДА ""0%"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС0) | КОГДА ""18%"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС18) | ИНАЧЕ ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС20) | КОНЕЦ КАК СтавкаНДС, | ТаблицаТоваров.УчетПоХарактеристикам КАК УчетПоХарактеристикам, | ТаблицаТоваров.КодТовара КАК КодТовара, | ТаблицаТоваров.ПодарочныйСертификат КАК ПодарочныйСертификат, | ТаблицаТоваров.ОсобенностиУчетаНоменклатуры КАК ОсобенностиУчетаНоменклатуры |ИЗ | (ВЫБРАТЬ | МАКСИМУМ(ТаблицаТоваров.Наименование) КАК Наименование, | МАКСИМУМ(ТаблицаТоваров.Артикул) КАК Артикул, | МАКСИМУМ(ТаблицаТоваров.СтавкаНДС) КАК СтавкаНДС, | МАКСИМУМ(ТаблицаТоваров.УчетПоХарактеристикам) КАК УчетПоХарактеристикам, | ТаблицаТоваров.КодТовара КАК КодТовара, | МАКСИМУМ(ТаблицаТоваров.ПодарочныйСертификат) КАК ПодарочныйСертификат, | МАКСИМУМ(ТаблицаТоваров.ОсобенностиУчетаНоменклатуры) КАК ОсобенностиУчетаНоменклатуры | ИЗ | ТаблицаТоваров КАК ТаблицаТоваров | ГДЕ | НЕ ТаблицаТоваров.Удалено | | СГРУППИРОВАТЬ ПО | ТаблицаТоваров.КодТовара) КАК ТаблицаТоваров | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты | ПО ТаблицаТоваров.КодТовара = НоменклатураДополнительныеРеквизиты.Значение | И (НоменклатураДополнительныеРеквизиты.Свойство = &Свойство)"; Запрос.УстановитьПараметр("Свойство", ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя","КодИнтеграции_Номенклатура")); Выборка = Запрос.Выполнить().Выбрать();
- Странная группировка по коду товара с поиском максимумов нечисловых полей. Но это сделано просто, чтобы схлопнуть разрез по размерам (характеристикам), который сейчас не нужен, в общем это не ошибка.
- Ужасный архитектурный просчет. Идентификатор номенклатуры внешней базы хранится в табличной части дополнительных реквизитов.
- При этом тип данных реквизита "Значение" является составным, включая как примитивные, так и ссылочные типы. Хотя в нашем случае хранятся идентификаторы, то есть строки, приведение типа в запросе не выполняется.
- Если уж автор индексирует ключевое поле временной таблицы загружаемых данных, которая в сущности небольшая, то почему бы не попробовать сделать то же самое с большой таблицей дополнительных реквизитов перед соединением.
Исправив эти вполне очевидные недочеты, даже без изменения архитектуры, получаем увеличение производительности на неких контрольных данных с двух часов до нескольких секунд. Отдельные запросы я не объединил в пакет, чтобы между ними вставлять журналирование времени работы. Группировку и соединение я тоже на всякий случай разделил, больше даже для наглядности. Вот что получилось в итоге...
Запрос = Новый Запрос(); Запрос.МенеджерВременныхТаблиц = мМенеджерВременныхТаблиц; Запрос.Текст = "ВЫБРАТЬ | ТаблицаТоваров.Артикул КАК Артикул, | ТаблицаТоваров.Наименование, | ТаблицаТоваров.Размер, | ТаблицаТоваров.СтавкаНДС, | ТаблицаТоваров.УчетПоХарактеристикам, | ТаблицаТоваров.Штрихкод, | ТаблицаТоваров.Удалено, | ТаблицаТоваров.КодТовара КАК КодТовара, | ТаблицаТоваров.ПодарочныйСертификат, | ТаблицаТоваров.ОсобенностиУчетаНоменклатуры | |ПОМЕСТИТЬ ТаблицаТоваров | |ИЗ | &ТаблицаТоваров КАК ТаблицаТоваров |"; Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаДанных); Запрос.Выполнить(); Запрос = Новый Запрос(); Запрос.МенеджерВременныхТаблиц = мМенеджерВременныхТаблиц; Запрос.Текст = "ВЫБРАТЬ | ТаблицаТоваров.Артикул КАК Артикул, | ТаблицаТоваров.Наименование КАК Наименование, | ВЫБОР ТаблицаТоваров.СтавкаНДС | КОГДА ""10%"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС10) | КОГДА ""Без НДС"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.БезНДС) | КОГДА ""0%"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС0) | КОГДА ""18%"" | ТОГДА ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС18) | ИНАЧЕ ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС20) | КОНЕЦ КАК СтавкаНДС, | ТаблицаТоваров.УчетПоХарактеристикам КАК УчетПоХарактеристикам, | ТаблицаТоваров.КодТовара КАК КодТовара, | ТаблицаТоваров.ПодарочныйСертификат КАК ПодарочныйСертификат, | ТаблицаТоваров.ОсобенностиУчетаНоменклатуры КАК ОсобенностиУчетаНоменклатуры | |ПОМЕСТИТЬ ТаблицаТоваровСвернутая | |ИЗ | (ВЫБРАТЬ | МАКСИМУМ(ТаблицаТоваров.Наименование) КАК Наименование, | МАКСИМУМ(ТаблицаТоваров.Артикул) КАК Артикул, | МАКСИМУМ(ТаблицаТоваров.СтавкаНДС) КАК СтавкаНДС, | МАКСИМУМ(ТаблицаТоваров.УчетПоХарактеристикам) КАК УчетПоХарактеристикам, | ТаблицаТоваров.КодТовара КАК КодТовара, | МАКСИМУМ(ТаблицаТоваров.ПодарочныйСертификат) КАК ПодарочныйСертификат, | МАКСИМУМ(ТаблицаТоваров.ОсобенностиУчетаНоменклатуры) КАК ОсобенностиУчетаНоменклатуры | ИЗ | ТаблицаТоваров КАК ТаблицаТоваров | ГДЕ | НЕ ТаблицаТоваров.Удалено | | СГРУППИРОВАТЬ ПО | ТаблицаТоваров.КодТовара) КАК ТаблицаТоваров |ИНДЕКСИРОВАТЬ ПО КодТовара"; |"; Запрос.Выполнить(); Запрос = Новый Запрос(); Запрос.МенеджерВременныхТаблиц = мМенеджерВременныхТаблиц; Запрос.Текст = "ВЫБРАТЬ | ТаблицаДопРеквизитов.Ссылка КАК Ссылка, | Выразить(ТаблицаДопРеквизитов.Значение КАК Строка(25)) КАК КодИнтеграции | |ПОМЕСТИТЬ ТаблицаДопРеквизитов | |ИЗ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ТаблицаДопРеквизитов |ИНДЕКСИРОВАТЬ ПО КодИнтеграции |"; Запрос.Выполнить(); Запрос = Новый Запрос(); Запрос.МенеджерВременныхТаблиц = мМенеджерВременныхТаблиц; Запрос.Текст = "ВЫБРАТЬ | ТаблицаТоваровСвернутая.Артикул КАК Артикул, | ТаблицаТоваровСвернутая.Наименование КАК Наименование, | ТаблицаДопРеквизитов.Ссылка КАК Номенклатура, | ТаблицаТоваровСвернутая.СтавкаНДС КАК СтавкаНДС, | ТаблицаТоваровСвернутая.УчетПоХарактеристикам КАК УчетПоХарактеристикам, | ТаблицаТоваровСвернутая.КодТовара КАК КодТовара, | ТаблицаТоваровСвернутая.ПодарочныйСертификат КАК ПодарочныйСертификат, | ТаблицаТоваровСвернутая.ОсобенностиУчетаНоменклатуры КАК ОсобенностиУчетаНоменклатуры |ИЗ | ТаблицаТоваровСвернутая КАК ТаблицаТоваровСвернутая | ЛЕВОЕ СОЕДИНЕНИЕ (Выбрать * из ТаблицаДопРеквизитов) КАК ТаблицаДопРеквизитов | ПО ТаблицаТоваровСвернутая.КодТовара = ТаблицаДопРеквизитов.КодИнтеграции |"; Выборка = Запрос.Выполнить().Выбрать();
Использование специального инструментария для анализа планов запросов часто является избыточным. Достаточно просто соблюдать несложные логичные принципы.
Нужно ли всегда тестировать масштабирование? Я считаю, что нет. Например, если запрос поиска среди 5 тысяч контрагентов работает быстро, а мы понимаем, что большой рост не предвидится, то в тестировании надуманного масштабирования нет необходимости. А вот если ежедневная загрузка номенклатуры 10-20 позиций идет быстро, то нужно обязательно проверить, что будет, если в базе источнике внесут массовые изменения и на загрузку поступит объем в 10-20 тысяч элементов.