Запрос работает в несколько раз медленнее при сравнении столбца BIT с 0 вместо 1 - PullRequest
2 голосов
/ 22 января 2009

Я использую представление, основанное на сложном запросе с 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 выполняется быстро. Быстрая фильтрация по другим столбцам.

Ответы [ 4 ]

1 голос
/ 22 января 2009

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

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

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

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

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

1 голос
/ 25 августа 2017

Я знаю, что это очень старый вопрос, но если кто-то все еще ищет решение, вы можете попробовать это. Недавно я столкнулся с той же проблемой, и это был относительно простой запрос, всего три таблицы в соединении, самая большая из них с более чем 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 также было очень быстрым, поэтому я допустил это так (он использовался в отчете с предоставленным сравнительным значением в качестве параметра)

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

1 голос
/ 22 января 2009

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

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

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

0 голосов
/ 22 мая 2018

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

myColumn = CAST(1 AS BIT)

CAST(myColumn AS BIT) = 1

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

...