Запрос работает в несколько раз медленнее при сравнении столбца BIT с 0 вместо 1

StackOverflow https://stackoverflow.com/questions/468641

  •  19-08-2019
  •  | 
  •  

Вопрос

Я использую представление, основанное на сложном запросе с 17 объединениями (как внутренними, так и левыми / правыми внешними) и подзапросами. Все строки просмотра отображаются примерно за 5 секунд.

SELECT * FROM a_view;

Один из столбцов представления имеет тип BIT. И когда я фильтрую строки просмотра, сравнивая его с 1, запрос снова работает около 5 секунд.

SELECT * FROM a_view WHERE c = 1;

Но когда я сравниваю этот столбец BIT с 0, запрос работает около 50 секунд (в 10 раз медленнее).

SELECT * FROM a_view WHERE c = 0;

Этот запрос, который возвращает те же строки результатов, работает, как и ожидалось, около 10 секунд:

SELECT * FROM a_view 
EXCEPT
SELECT * FROM a_view WHERE c = 1;

Поэтому мне интересно, почему сравнение с 0 или FALSE занимает так много времени? Любые идеи, пожалуйста.

Сортировка в этом поле BIT выполняется быстро. Фильтрация по другим столбцам тоже быстрая.

Это было полезно?

Решение

Обычно существует несколько способов выполнения запроса, включающего соединения. Все современные СУБД осуществляют поиск по различным планам соединения, ища лучший план, оценивая затраты (время доступа к ЦП и диску) для каждого.

Проблема в том, что каждое дополнительное объединение в запросе увеличивает число возможных планов на увеличивающееся число, что приводит к двухфакторному (хуже, чем экспоненциальному) росту числа планов , который следует рассматривать как количество соединений увеличивается. По этой причине БД должна где-то ограничивать поиск, а это означает, что неоптимальные планы неизбежно выбираются для запросов, включающих много объединений.

Для справки, PostgreSQL прекращает оценку всех возможных планов после 12 соединений по умолчанию. SQL Server наверняка будет иметь подобное ограничение. Для 17 соединений потребуется (2 * 13) * (2 * 14) * (2 * 15) * (2 * 16) * (2 * 17) раз больше времени для оценки - этого более чем достаточно, чтобы перегружать любую СУБД, имеющую когда-либо существовал, или когда-либо будет .

Существует также тот факт, что БД оценивают затраты на основе приблизительной статистики, такой как количество различных значений в столбце и / или список из 10 наиболее распространенных значений в столбце. Все это в совокупности приводит к тому, что с увеличением числа объединений вероятность выбора наилучшей (или даже разумной) стратегии объединения уменьшается вниз .

Зачем вам нужно объединить 17 столов? Нет ли способа упростить схему БД?

Другие советы

SQL Server SQL Server помещает весь SQL-запрос представления в оператор SQL, который вы написали для представления, а затем пытается оптимизировать его.

Это может привести к ситуации, когда при c = 0 статистика используемых таблиц показывает, что существует гораздо больше строк, соответствующих этому предикату, чем при c = 1. Например, при c = 1 таблица, содержащая поле c, являющееся центром объединений, может вернуть только 5 совпадающих строк, что приводит к совершенно другому плану выполнения, чем если бы таблица возвращала 1 миллион строк (например, ситуация для с = 0).

Так что изучите планы выполнения для обоих. Также проверьте результаты профилирования сервера для обоих, так как при c = 0 может быть, что чтения намного больше, чем при c = 1, и возвращается гораздо больше результатов, чем при c = 1. Возврат всех строк может занять некоторое время, поэтому это может быть причиной того, что запрос медленнее.

Я знаю, что это очень старый вопрос, но если кто-то все еще ищет решение, вы можете попробовать это. Недавно я столкнулся с той же проблемой, и это был относительно простой запрос, всего три таблицы в соединении, самая большая из них с более чем 1000 строк. К сожалению, у меня не было привилегий для просмотра плана exec, поэтому мне пришлось импровизировать.

select * from subquery_x;

очень быстро, практически сразу закончил (вернул всего около 500 строк), как и должно быть

select * from subquery_x where column_x = 1  

dtto, очень быстрый, column_x - это ненулевой битовый столбец

select * from subquery_x where column_x = 0
select * from subquery_x where column_x != 1

должно вернуть около 300 строк, оба очень, очень медленные, на самом деле это заняло несколько минут !!

простое (но странное) решение - преобразовать столбец в выражении where в tinyint

select * from subquery_x where convert(tinyint,column_x) = 0

Я знаю, что это может иметь некоторые побочные эффекты с точки зрения производительности, но это сработало как чудо, сравнение преобразованного значения столбца с 1 также было очень быстрым, поэтому я допустил это так (он использовался в отчете с предоставленным сравниваемым значением) в качестве параметра)

Если кто-то знает, почему это происходит, дайте нам знать, я подозреваю, что это ошибка, но кто знает, может быть, это неприятная особенность: -)

Похоже на решение horcic.roman ... Я приведу значение или столбец к BIT для значительного увеличения в скорости.

myColumn = CAST(1 AS BIT)

CAST(myColumn AS BIT) = 1

Очевидно, довольно странно, учитывая, что столбец BIT NOT NULL.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top