Почему subquery и join так медленно
мне нужно выбрать строки из таблицы BUNDLES, которые имеют одно из нескольких значений SAP_STATE_ID. Эти значения зависят от того, предполагается ли экспортировать соответствующий статус SAP или нет.
этот запрос выполняется очень быстро (есть индекс в поле SAP_STATE_ID) -
SELECT b.* FROM BUNDLES b WHERE b.SAP_STATE_ID IN (2,3,5,6)
но... Я хотел бы получить список идентификаторов динамически, например:
SELECT b.* FROM BUNDLES b
WHERE b.SAP_STATE_ID IN
(SELECT s.SAP_STATE_ID FROM SAP_STATES s WHERE s.EXPORT_TO_SAP = 1)
и Ой, этот запрос внезапно занимает слишком много времени. Я бы ожидал, что SQL server сначала запустит подзапрос (это не зависит ни от чего из основного запроса), а затем запустите все, как в моем первом примере. Я попытался переписать его, чтобы использовать соединения вместо подзапроса:
SELECT b.* FROM BUNDLES b
JOIN SAP_STATES s ON (s.SAP_STATE_ID = b.SAP_STATE_ID)
WHERE s.EXPORT_TO_SAP = 1
но он имеет такую же плохую производительность. Похоже, что он запускает подзапрос для каждой строки таблицы BUNDLES или что-то в этом роде. Я не очень хорошо разбираюсь в планах исполнения, но я пытался. В нем говорится, что 81% стоимости-для сканирования индекса первичного ключа пакетов (я понятия не имею, почему он должен делать такую вещь, там поле BUNDLE_ID определено как первичный ключ, но оно вообще не отображается в запросе...)
у кого-нибудь есть объяснение, почему SQL server настолько "глупый"? Есть ли способ достичь того, что я хочу с хорошей производительностью, но без необходимости предоставлять статический список SAP_STATE_IDs?
скрипт для обеих таблиц и соответствующих индексов -http://mab.to/xbYiI0wKj
план выполнения подзапроса версия - http://mab.to/8Qh6gpdYZ
план запроса для версии с соединениями -http://mab.to/YCqeGCUbr
(по какой-то причине эти два плана выглядят одинаково, и оба предлагают создавать пакеты.Индекс SAP_STATE_ID, который уже есть)
3 ответов
Я уверен, что ваша статистика на столы. Если вы хотите, чтобы он работал в спешке, я бы написал запрос как:
SELECT b.*
FROM SAP_STATES s
INNER LOOP JOIN BUNDLES b
ON s.SAP_STATE_ID = b.SAP_STATE_ID
WHERE s.EXPORT_TO_SAP = 1
это заставляет вложенные петли присоединиться к SAP_STATES
какие фильтры на BUNDLES
когда вы используете таблицы (временные или физические), SQL engine строит статистику против него и, таким образом, имеет очень четкое представление о количестве строк в нем и который является лучшим подходом к выполнению для него. С другой стороны, вычисляемая таблица(суб-запрос) не имеет статистики против нее.
поэтому, хотя человеку может показаться простым вывести количество строк в нем," глупый " SQL-движок не знает обо всем этом. Теперь, переходя к вопросу,WHERE s.EXPORT_TO_SAP = 1
предложение делает разница здесь. Кластеризованный индекс сортируется и строится на SAP_STATE_ID, но для дополнительной проверки предложения WHERE у него нет опции, кроме сканирования всей таблицы(в конечном наборе данных)! Я уверен, что если бы вместо кластеризованного индекса, если бы был некластеризованный покрытый индекс в столбце SAP_STATE_ID, который покрывал поле EXPORT_TO_SAP, это могло бы сделать трюк. Поскольку кластеризованные индексные проверки обычно плохи для производительности, я бы предложил вам принять следующее подход:
SELECT s.SAP_STATE_ID
into #Sap_State
FROM SAP_STATES s WHERE s.EXPORT_TO_SAP = 1
SELECT b.* FROM BUNDLES b
join #Sap_State a on a.sap_state_id = b.sap_state_id
Так как по какой-то причине возникли проблемы с переходом на mab.to,
Я бы предложил обеспечить следующее
table index
sap_states (export_to_sap, sap_state_id )
bundles (sap_state_id)
select
b.*
from
sap_states ss
join bundles b
on ss.sap_state_id = b.sap_state_id
where
ss.export_to_sap = 1