Запретить SQL Server 2017 от выполнения некоррелированных подзапросов в цикле - PullRequest
0 голосов
/ 13 февраля 2019

У нас есть мультитенантная база данных, разделение данных достигается с помощью ключа, прикрепленного ко всем таблицам.Количество строк в каждой таблице может сильно отличаться для каждого арендатора (от 0 до 500000+).После перехода на SQL Server 2017 иногда используются очень плохие планы выполнения.У нас не было проблем с ранее использовавшимся SQL Server 2008.

Самая большая проблема заключается в том, что иногда некоррелированные подзапросы выполняются в цикле для каждой строки внешнего запроса.Подзапрос займет 0 секунд, а внешний запрос с использованием IN (список идентификаторов) также займет 0 секунд, но вместе они выполняются в течение нескольких минут.

Я пробовал разные решения:

  • OPTION(RECOMPILE)
  • Изменение на JOIN и OPTION(FORCE ORDER)
  • UPDATE STATISTICS для таблиц
  • Параметр Sniffing включен, но не делаетразница

Ни один из них не решил проблему.

Пример запроса:

SELECT address.adrId, address.adrCity FROM address 
WHERE address.orgId=1 AND
address.adrId IN (SELECT instAddress.adrId 
                    FROM instAddress
                    WHERE instAddress.instId = 12345 
                    AND instAddress.orgId = 1) 

ОБНОВЛЕНИЕ: фактический план выполнения, подсвеченное число выполненных и примерное количество выполнений,enter image description here

Можно ли как-то сказать SQL Server всегда выполнять подзапрос первым и только один раз?

ОБНОВЛЕНИЕ:

Этот один запрос изменился на правильный план выполнения после последнего обновления статистики.Этот и другие похожие запросы генерируются нашим приложением динамически.Мы хотели бы помешать нам переписать весь уровень базы данных.

Я выбрал этот запрос для примера, потому что он был самым базовым при возникновении проблемы.Другой пример (выборка переводов для некоторых имен), где независимый подзапрос выполняется в циклах для доступного фактического плана выполнения внешнего запроса: https://pastebin.com/tbB0vPUZ

select institutions.inst_id , institutions.inst_nr , inst_name , 
translations1.trans_to as trans_to , translations2.trans_to as trans_to2 , 
translations3.trans_to as trans_to3 , translations4.trans_to as trans_to4 
from institutions 
left join translations translations1 on institutions.inst_nr = translations1.trans_from and translations1.lang_locale = @0 and translations1.org_id = @1 
left join translations translations2 on institutions.inst_nr = translations2.trans_from and translations2.lang_locale = @2 and translations2.org_id = @3 
left join translations translations3 on institutions.inst_name = translations3.trans_from and translations3.lang_locale = @4 and translations3.org_id = @5 
left join translations translations4 on institutions.inst_name = translations4.trans_from and translations4.lang_locale = @6 and translations4.org_id = @7 
where ( institutions.org_id = @8 and lcd_id = @12 
and exists ( 
    select inst_id_partner 
    from agreements 
    where agreements.inst_id_partner = institutions.inst_id and org_id = @13 and agree_id in ( 
        select agree_id 
        from agreements 
        where org_id = @14 and pers in ( @15 , @16 ) and art in ( @17 , @18 ) and prog_id not in ( 
            select prog_id 
            from programs 
            where org_id = @19 and is_not_part_of_all_prog = @20 ) 
        and agree_id in ( 
            select agree_id 
            from year_agree where year_id = @21 and sem_id = @22 ) 
        and ( agree_id not in ( 
            select agree_id 
            from agree_stat 
            where org_id = @23 ) 
        or agree_id in ( 
            select agree_id 
            from agree_stat 
            where org_id = @24 and agree_stat.year_id = @25 and agree_stat.sem_id = @26 and agree_stat.count1 < agreements.total ) ) 
        and inst_id in ( @27 ) ) 
) 
order by translations1.trans_to

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

Вы также можете достичь этого с помощью стандартного соединения:

SELECT a.adrId, a.adrCity 
FROM address  a
inner join instAddress i on i.orgId = a.ordId and i.adrId = a.adrId
where i.instId = 12345 and a.orgId = 1
0 голосов
/ 13 февраля 2019

Я думаю, что переписывание исходного запроса для использования EXISTS может привести к более надежным планам запросов:

SELECT a.adrId, a.adrCity FROM address  a
WHERE EXISTS ( SELECT *
                    FROM instAddress i
                    WHERE i.instId = 12345 
                    and i.orgId=1
                    AND i.orgId = d.orgId 
                    AND i.adrId = a.adrId 

             )

Другое наблюдение заключается в том, что такой индекс увеличит вероятность того, чтоОптимизатор запросов установит коррелированный запрос как исходный для выполнения, а затем извлечет строки из [address]

CREATE INDEX IX_1 ON instAddress (instId) INCLUDE (orgid, adrId )

Другая возможная причина - устаревшая статистика.

Согласнона OP:

Запрос в начальном примере был одним из примерно 10 похожих запросов, для которых возникла эта проблема, некоторые из них появляются и исчезают при изменении статистики или параметров, но в конечном итоге всплывающее усилениечерез некоторое время

Если уровень совместимости ниже 130 , порог по умолчанию для автоматически обновляемой статистики составляет 20% измененных строк.Это особенно нехорошо для больших таблиц и может быть исправлено с помощью:

DBCC TRACEON (2371, -1)

Другая возможная причина : Новый оценщик мощности и коррелированные запросы.

С момента перехода на SQL Server 2017 иногда используются очень плохие планы выполнения.У нас не было проблем с ранее использовавшимся SQL Server 2008.

Старая (устаревшая) оценка количества элементов (которая была доступна в SQL 2008) может быть включена в качестве подсказки запроса на уровне базы данных.или на уровне экземпляра, с помощью флага трассировки

SELECT .. FROM .. WHERE ..
OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'));  

или на уровне базы данных:

USE DB
ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = ON

Похожие:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...