Головоломка оптимизатора запросов SQL Server 2008 R2
У нас есть две таблицы, каждая из которых содержит 9 миллионов строк. 70 000 строк разные, остальные одинаковые.
Это быстро, 13 секунд,
select * from bigtable1
except select * from similar_bigtable2
Это сортирует вывод и также быстро, 13 секунд,
select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column
Пока это невероятно медленно
;with q as (
select * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
И даже «трюк», который я иногда использую, чтобы намекнуть SQL Server, что ему необходимо предварительно рассчитать определенную часть запроса, прежде чем он будет выполнен, не работает и также приводит к медленному запросу:
;with q as (
select top 100 percent * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
Глядя на планы запросов, найти причину несложно:
SQL Server помещает два сорта по 9 миллионов строк перед хэш-соответствием, в то время как я бы предпочел, чтобы после хеш-соответствия было добавлено только один вид из 70 000 строк .
Итак, вопрос: как я могу поручить оптимизатору запросов сделать это?
источник
EXCEPT
(напримерOUTER JOIN
)? Я понимаю, что синтаксис менее удобен, но вы можете лучше использовать подсказки индексации / объединения (или вам это может не понадобиться). Альтернатива, которую вы используете сейчас (сначала добавьте в таблицу #temp), является последним средством, но в некоторых случаях это единственный способ заставить оптимизатор полностью разделить две части запроса так, как вам нужно.Ответы:
Основное различие между этими двумя планами запросов заключается в разнице между Hash Match и Merge Join. Hash Match более эффективен, и, как вы можете видеть, запрос выполняется быстрее в варианте 1 (без использования CTE).
CTE - отличный инструмент, но он кажется неэффективным в двух случаях: сложные предикаты или неуникальный родительский / дочерний ключ. В вашем случае нет уникального ключа, и SQL-сервер должен сначала отсортировать наборы данных, чтобы выполнить ваше требование. Посмотрите на ссылку ниже, которая расскажет вам больше об этой проблеме: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx
Поэтому кажется, что вы должны либо принять его медлительность, либо переписать логику с циклом WHILE, что может быть более эффективным.
источник
Попробуйте это, лучше?
источник
Это не идеальное решение, но если вы не можете структурировать tsql для генерации эффективного плана, вы можете установить руководство плана, чтобы заставить план, который вы хотите. Это будет означать, что если станет доступен более эффективный план, SQL не будет его рассматривать, но это вариант.
источник