Здравствуйте. Дано: есть документ "Накладная", регистр сведений (2млн записей) "Идентификатор" с измерением "объект" составного типа (один из которых документ "накладная". Запрос строится левым соединением Документ - Регистр. Вопрос: Как в запросе корректно наложить условия для максимального быстродействия?
"ВЫБРАТЬ
| ВЫРАЗИТЬ(Идентификатор.Объект КАК Документ.Накладная) КАК Ссылка
|ИЗ
| РегистрСведлений.Идентификатор КАК Идентификатор
|ГДЕ
| ВЫРАЗИТЬ(Идентификатор.Объект КАК Документ.Накладная) = &ВотЭтуСсылкуИщу"
А при соединении вообще ничего не надо. там связь по значению идет, соответственно никакой дополнительной типизации в запросе не надо
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО (втЗаказы.Ссылка = (ВЫРАЗИТЬ(ИдентификаторыОбмена.Объект КАК Документ.усЗаказНаОтгрузку)))
ГДЕ
ИдентификаторыОбмена.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
ВЫБРАТЬ
ИдентификаторыОбмена.Объект,
ИдентификаторыОбмена.OID
ПОМЕСТИТЬ втИдентификаторы
ИЗ
РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ГДЕ
ИдентификаторыОбмена.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втЗаказы.Ссылка,
ИдентификаторыОбмена.OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ втИдентификаторы КАК ИдентификаторыОбмена
ПО КорректировкаЗаписейРегистров.Ссылка = втИдентификаторы.Объект
(12) А база точно содержит 2КК записей по регистру? Что-то по времени уж очень мало на обоих примерах. На "малых" базах результат может быть иным.
Хотя, конечно, может уже как-то оптимизировали работу соединения в последних версиях платформы. Раньше сравнение по составному типу разбивалось на несколько соединений в количестве участвующих типов в составе.
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
Этот запрос выполняется примерно 90-100 мс
А этот около 60 -80 мс:
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
ГДЕ
ИдентификаторыОбмена.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
Показать
разве сперва не выполняется условие соединения а потом накладывается условие из "ГДЕ"? почему этот вариант влияет у меня на производительность?
(13) Меньше. Но будет, только в худшую. Смотрите сами получаем во временную не индексированную таблицу миллион записей, из которой SQL нужно найти 50 строк. Будет table scan. В случае соединения регистра index seek.
(15) У них и результат разный будет. В первом левое соединение, во втором внутреннее.
SEL ECT
T1._Q_000_F_000RRef,
T2._Fld23070
FR OM #tt4 T1 WITH(NOLOCK)
INNER JOIN dbo._InfoRg23069 T2
ON (0x08 = T2._Fld23071_TYPE AND 0x0000002D = T2._Fld23071_RTRef AND T1._Q_000_F_000RRef = T2._Fld23071_RRRef)
WHERE ((T2._Fld14043 = @P1)) AND ((T2._Fld23071_TYPE = 0x08 AND T2._Fld23071_RTRef = 0x0000002D))
План выполнения точно такой же как и в первом случае
(15) Возможно срабатывает оптимизация. Попробуйте ещё так:
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
И (ИдентификаторыОбмена.Объект ССЫЛКА Документ.усЗаказНаОтгрузку)
SEL ECT
T1._Q_000_F_000RRef,
T2._Fld23070
FR OM #tt4 T1 WITH(NOLOCK)
LEFT OUTER JOIN dbo._InfoRg23069 T2
ON ((0x08 = T2._Fld23071_TYPE AND 0x0000002D = T2._Fld23071_RTRef AND T1._Q_000_F_000RRef = T2._Fld23071_RRRef)) AND (T2._Fld14043 = @P1)
(17) 1С сама добавляет условие по типу (выделил жирным)
SEL ECT
T1._Q_000_F_000RRef,
T2._Q_001_F_001
FR OM #tt4 T1 WITH(NOLOCK)
LEFT OUTER JOIN #tt57 T2 WITH(NOLOCK)
ON (0x08 = T2._Q_001_F_000_TYPE AND 0x0000002D = T2._Q_001_F_000_RTRef AND T1._Q_000_F_000RRef = T2._Q_001_F_000_RRRef)
Если не сложно, можете коротко объяснить?) я относительно недавно начал постигать азы производительных запросов, собираюсь настроить у себя тоже тех журнал для изучения планов
(23)в целом все понятно. Сейчас у нас как раз таки используется (9), который вы описали и производительность нормальная. Но наш скуль специалист поизучал как внутри sql крутится и сказал что запрос не оптимальный (строится куча левых соединений). Хотел бы узнать один момент:
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
ГДЕ
ИдентификаторыОбмена.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
Показать
этот вариант по моим исследованиям, работает быстрее. Подскажите, в планировщике он строит только одно левое соединение или на все 20 составных объектов?
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
отрабатывал бы без множественных соединений?
Можно ли вообще, например, сейчас поставить флаг, таблица с 2 млн записей нормально отреструктуризируется?
(32) таблица, по сути, не должна трогаться (вы же не меняете её структуру, добавляя или удаляя поля, или меняя их типы), должна добавиться ещё одна таблица (скрытая) индексов, т.е. размер базы вырастет.
Но, перед любыми манипуляциями со структурой БД рекомендуется резервная копия.
(24) Соединение будет в любом случае одно не знаю где он кучу соединений увидел. Вы лучше показали бы как строится таблица втЗаказы, может там увидим источник проблемы.
(42)там разные объекты соединяются, чтобы каждый мог получить свой идентификатор. Я просто тестирую отдельный кусок запроса, пытаясь вычислить производительность
(44) Понимаете, мне скинули этот файл, сказали "запросы обмена к этому регистру не оптимальны, оптимизируй". Вот я и хотел узнать, как корректно типизировать составной регистр. Сейчас у нас система работает на том примере запроса, который предложили вы, наш sql-щик говорит "куча множественных соединений". В целом запрос выполняется быстро, в теории "Объект Ссылка Документ.ЗаказНаОтгрузку" должна была ее еще улучшить, а получил обратных эффект.
(46)Огромное спасибо за помощь! У себя настроил трассировку запросов, теперь вижу что платформа сама походу типизирует мой кусок запроса. Нужно весь запрос целиком отлаживать и смотреть схемы. Нету ли у вас ссылки на какую либо статью, где описано как правильно расшифровывать файлы трассировки? (что означает hash match, index scan ....итд)
ВЫБРАТЬ
усЕдиницыИзмерения.Ссылка КАК Ссылка,
усЕдиницыИзмерения.ПометкаУдаления КАК ПометкаУдаления,
усЕдиницыИзмерения.Код КАК Код,
усЕдиницыИзмерения.Наименование КАК Наименование,
усЕдиницыИзмерения.КодFirebird КАК КодFirebird,
ск_ИдентификаторыОбменаВнешнегоИсточника.US_OID КАК US_OID
ИЗ
Справочник.усЕдиницыИзмерения КАК усЕдиницыИзмерения
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ск_ИдентификаторыОбменаВнешнегоИсточника
ПО (ск_ИдентификаторыОбменаВнешнегоИсточника.Объект.Ссылка = усЕдиницыИзмерения.Ссылка)
ГДЕ
НЕ усЕдиницыИзмерения.ПометкаУдаления
И усЕдиницыИзмерения.КодFirebird = &КодFirebird
Показать
Убрал "Ссылка" тут ск_ИдентификаторыОбменаВнешнегоИсточника.Объект.Ссылка и запрос начал работать нормально
Индексирование полей составного типа Создание индексов по полям составных типов имеет важную особенность, которая при неосторожном использовании может отрицательно сказаться на производительности информационной базы.
Если в индекс входит поле составного типа, для которого задано несколько различных типов (отличных от ссылок или ссылки хотя бы с одним типом, отличным от ссылок), то вместо каждого дополнительного индекса, включающего это поле, будет создано столько дополнительных индексов, сколько различных типов содержится в описании типа этого поля. Такой подход позволяет уменьшить длину ключа в индексе и использовать построенные индексы в операциях сравнения полей составных типов.
Хотя в вашем случае там только ссылки должны быть.
В (5) в запросе можно добавить индексацию по полю Объект, возможно это ускорит выполнение запроса.
ВЫБРАТЬ
ВЫБОР
КОГДА ск_ИдентификаторыОбменаВнешнегоИсточника.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
ТОГДА ВЫРАЗИТЬ(ск_ИдентификаторыОбменаВнешнегоИсточника.Объект КАК Документ.усЗаказНаОтгрузку)
КОНЕЦ КАК Объект,
ск_ИдентификаторыОбменаВнешнегоИсточника.OID КАК OID,
ск_ИдентификаторыОбменаВнешнегоИсточника.US_OID КАК US_OID
ПОМЕСТИТЬ Идентификаторы
ИЗ
РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ск_ИдентификаторыОбменаВнешнегоИсточника
ГДЕ
ск_ИдентификаторыОбменаВнешнегоИсточника.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
Идентификаторы.OID КАК OID,
Идентификаторы.US_OID КАК US_OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ Идентификаторы КАК Идентификаторы
ПО втЗаказы.Ссылка = Идентификаторы.Объект
Показать
Производительность запроса упала, а индексы не дает добавить на составной тип
ВЫБРАТЬ
ВЫРАЗИТЬ(ск_ИдентификаторыОбменаВнешнегоИсточника.Объект КАК Документ.усЗаказНаОтгрузку) КАК Объект,
ск_ИдентификаторыОбменаВнешнегоИсточника.OID КАК OID,
ск_ИдентификаторыОбменаВнешнегоИсточника.US_OID КАК US_OID
ПОМЕСТИТЬ Идентификаторы
ИЗ
РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ск_ИдентификаторыОбменаВнешнегоИсточника
ГДЕ
ск_ИдентификаторыОбменаВнешнегоИсточника.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
ИНДЕКСИРОВАТЬ ПО
Объект
Показать
В использовании ВЫРАЗИТЬ также не уверен, что это поможет увеличить скорость выполнения, а не наоборот.
Впервые о таком слышу. Может причина в установленном флаге Ведущее?
Но если так, то по данному измерению таблица индексации итак должна была создаться.
(36) ссылка на ImgBB такая ошибка была, но да, когда типизировал поля она пропала.
меня сейчас интересует этот запрос:
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ИдентификаторыОбмена.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
ГДЕ
ИдентификаторыОбмена.Объект ССЫЛКА Документ.усЗаказНаОтгрузку
Показать
т.к. это сейчас единственный вариант, который отрабатывает быстрее остальных. Если он с точки зрений оптимальности хороший и не образует множественные соединения, то буду пробовать его в тестовую базу. Эх надо самому настроить тех журнал, что осматривать планы запросов.
ВЫБРАТЬ
ИдентификаторыОбмена.Объект КАК Объект,
ИдентификаторыОбмена.OID КАК OID
Поместить ТабИдентификаторы
ИЗ
РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ГДЕ ИдентификаторыОбмена.Объект В (Выбрать втЗаказы.Ссылка Из втЗаказы КАК втЗаказы)
;
ВЫБРАТЬ
втЗаказы.Ссылка КАК Ссылка,
ТабИдентификаторы.OID КАК OID
ИЗ
втЗаказы КАК втЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ ТабИдентификаторы КАК ТабИдентификаторы
ПО втЗаказы.Ссылка = ТабИдентификаторы.Объект
(20) Т.е. в моем примере стоимость соединения меньше, хотя стоимость просмотра таблицы больше, чем поиск по индексу. Кроме того, что насчёт Probe Residual в Hash Match? Есть или нет? Интересно бы глянуть. Я бы и сам этим занялся, но к сожалению на данный момент не имею возможности, а было бы очень интересно окончательно прояснить этот вопрос оптимизации.
ВЫБРАТЬ
втЗаказы.Ссылка,
ИдентификаторыОбмена.OID
ИЗ
втЗаказы КАК втЗаказы
РегистрСведений.ск_ИдентификаторыОбменаВнешнегоИсточника КАК ИдентификаторыОбмена
ПО втЗаказы.Ссылка = ИдентификаторыОбмена.Объект
"ВЫБРАТЬ
| ВЫРАЗИТЬ(Идентификатор.Объект КАК Документ.Накладная) КАК Ссылка
|ИЗ
| РегистрСведлений.Идентификатор КАК Идентификатор
|ГДЕ
| ВЫРАЗИТЬ(Идентификатор.Объект КАК Документ.Накладная) = &ВотЭтуСсылкуИщу"
ссылка она и есть ссылка, прямо по ней и ищите
вот это тоже самое
"ВЫБРАТЬ
| Идентификатор.Объект КАК Ссылка
|ИЗ
| РегистрСведлений.Идентификатор КАК Идентификатор
|ГДЕ
| Идентификатор.Объект = &ВотЭтуСсылкуИщу"