Производительность a = 0 и b = 0 и… z = 0 против a + b + c + d = 0

20

Это простой вопрос, на который я не могу найти ответ.

С точки зрения производительности, если у меня есть такое WHEREусловие a=0 and b=0 and ... z=0, могу ли я получить какую-либо производительность, если я заменю это условие на a+b+...+z=0?

Другими словами, есть ли прирост производительности путем замены следующего

Select * 
From MyTable 
Where A=0 and B=0 and C=0 and D=0...

С

Select * 
From MyTable 
Where A+B+C+D=0...

Я знаю, что это может зависеть от индексов, но для этого, скажем так, индексов не существует. Работает ли арифметический оператор (+) лучше, чем логический оператор «ИЛИ» или «И»?

У меня сложилось впечатление, что сложение выполняется лучше, чем несколько условий с AND или OR.

Результаты теста

На таблице 4,2 миллиона строк

Возвращаемые строки, где A = 0, B = 0 и C = 0 -> 351748 строк

Добавление (A + B + C = 0) заняло 5 секунд, в то время как логические условия A = 0 и B = 0 и C = 0 заняли 11 секунд.

С другой стороны

Возвращение строк, где A <> 0 B <> 0 или C <> 0 -> 3829750 Строки 58 секунд

Возврат строк, где F65 + F67 + f64 <> 0 -> 3829750 Строки 57 секунд

Для OR, кажется, нет существенной разницы.

Я согласен с ГБН:

Если A равно -1, а B равно 1, A + B = 0, но A = 0 и B = 0 ложно

и с AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Даже если вы ожидаете только положительные значения, если столбец принимает отрицательные значения, вы должны предположить, что вы можете столкнуться с одним

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

A = плавать, B = деньги и C = плавать. Используемый запрос как показано. В моем случае все положительные числа. Нет индексов. Просто логично, что сложение будет быстрее, чем логические условия!

JohnG
источник
Это булевы? Сколько столбцов вы говорите о 4 (в примерах) или 26 (в заголовке)? Это имеет значение. Какая версия SQL Server? Где FLOAT и ДЕНЬГИ вступают в игру? Сколько строк мы предполагаем? Этот вопрос имеет массу факторов.
Эван Кэрролл
@Evan Carroll Они не булевы, это неиндексированные числа (int, float, money и т. Д.). Независимо от версии SQL (SQL2012 и более поздних версий), количества строк или столбцов, вопрос состоял в том, чтобы выяснить, какой оператор выполняет лучше - логические и арифметические операторы. Как видите, Макс Вернон отлично демонстрирует теорию на своих примерах.
JohnG

Ответы:

46

В своем вопросе вы детализируете подготовленные вами тесты, в которых вы «доказываете», что опция добавления выполняется быстрее, чем сравнение отдельных столбцов. Я подозреваю, что ваша методология тестирования может быть ошибочной по нескольким причинам, на что ссылались @gbn и @srutzky.

Во-первых, вам нужно убедиться, что вы не тестируете SQL Server Management Studio (или любой другой клиент, который вы используете). Например, если вы запускаете SELECT *из таблицы с 3 миллионами строк, вы в основном тестируете способность SSMS извлекать строки из SQL Server и отображать их на экране. Вам гораздо лучше использовать что-то подобное, SELECT COUNT(1)что устраняет необходимость протягивать миллионы строк по сети и отображать их на экране.

Во-вторых, вам нужно знать о кеше данных SQL Server. Как правило, мы тестируем скорость чтения данных из хранилища и обработки этих данных из холодного кэша (т. Е. Буферы SQL Server пусты). Время от времени имеет смысл проводить все тестирование с использованием «теплого кэша», но вам необходимо явно подходить к тестированию с учетом этого.

Для теста с холодным кэшем необходимо запускать CHECKPOINTи DBCC DROPCLEANBUFFERSперед каждым запуском теста.

Для теста, который вы задали в своем вопросе, я создал следующий тестовый стенд:

IF COALESCE(OBJECT_ID('tempdb..#SomeTest'), 0) <> 0
BEGIN
    DROP TABLE #SomeTest;
END
CREATE TABLE #SomeTest
(
    TestID INT NOT NULL
        PRIMARY KEY 
        IDENTITY(1,1)
    , A INT NOT NULL
    , B FLOAT NOT NULL
    , C MONEY NOT NULL
    , D BIGINT NOT NULL
);

INSERT INTO #SomeTest (A, B, C, D)
SELECT o1.object_id, o2.object_id, o3.object_id, o4.object_id
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;

SELECT COUNT(1) 
FROM #SomeTest;

Это возвращает подсчет 260 144 641 на моей машине.

Чтобы проверить метод «сложения», я запускаю:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE (st.A + st.B + st.C + st.D) = 0;
GO
SET STATISTICS IO, TIME OFF;

Вкладка сообщений показывает:

Таблица «#SomeTest». Сканирование 3, логическое чтение 1322661, физическое чтение 0, чтение с опережением 1313877, логическое чтение 1, физическое чтение 0, чтение с опережением 0.

Время выполнения SQL Server: время ЦП = 49047 мс, прошедшее время = 173451 мс.

Для теста "дискретные столбцы":

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE st.A = 0
    AND st.B = 0
    AND st.C = 0
    AND st.D = 0;
GO

SET STATISTICS IO, TIME OFF;

снова с вкладки сообщений:

Таблица «#SomeTest». Сканирование 3, логическое чтение 1322661, физическое чтение 0, чтение с опережением 1322661, логическое чтение LOB 0, физическое чтение LOB 0, предварительное чтение LOB чтения 0.

Время выполнения SQL Server: время ЦП = 8938 мс, прошедшее время = 162581 мс.

Из приведенной выше статистики вы можете видеть второй вариант, когда дискретные столбцы сравниваются с 0, истекшее время примерно на 10 секунд короче, а время процессора примерно в 6 раз меньше. Большая длительность моих тестов выше, в основном, является результатом чтения большого количества строк с диска. Если вы уменьшите число строк до 3 миллионов, вы увидите, что отношения остаются примерно такими же, но затраченное время заметно падает, поскольку дисковый ввод-вывод оказывает гораздо меньшее влияние.

С помощью метода «Дополнение»:

Таблица «#SomeTest». Сканирование 3, логическое чтение 15255, физическое чтение 0, чтение с опережением 0, логическое чтение с 0, физическое чтение с 0, чтение с опережением 0.

Время выполнения SQL Server: время ЦП = 499 мс, прошедшее время = 256 мс.

С помощью метода «дискретные столбцы»:

Таблица «#SomeTest». Сканирование 3, логическое чтение 15255, физическое чтение 0, чтение с опережением 0, логическое чтение с 0, физическое чтение с 0, чтение с опережением 0.

Время выполнения SQL Server: время ЦП = 94 мс, прошедшее время = 53 мс.

Что будет действительно очень важно для этого теста? Соответствующий индекс, такой как:

CREATE INDEX IX_SomeTest ON #SomeTest(A, B, C, D);

Метод «сложения»:

Таблица «#SomeTest». Сканирование 3, логическое чтение 14235, физическое чтение 0, чтение с опережением 0, логическое чтение с 0, физическое чтение с 0, чтение с опережением 0.

Время выполнения SQL Server: время ЦП = 546 мс, прошедшее время = 314 мс.

Метод «дискретных столбцов»:

Таблица «#SomeTest». Сканирование 1, логическое чтение 3, физическое чтение 0, чтение с опережением 0, логическое чтение с бита 0, физическое чтение с бита 0, чтение с опережением чтения 0.

Время выполнения SQL Server: время ЦП = 0 мс, прошедшее время = 0 мс.

План выполнения для каждого запроса (с указанным индексом на месте) довольно показателен.

Метод сложения, который должен выполнить сканирование всего индекса:

введите описание изображения здесь

и метод «дискретных столбцов», который может искать первую строку индекса, где находится ведущий столбец индекса A, равен нулю:

введите описание изображения здесь

Макс Вернон
источник
24

Допустим, у вас есть индекс на A, B, C и D. Также можно отфильтровать.

Это более вероятно использовать индекс, чем сложение.

Where A=0 and B=0 and C=0 and D=0

В других новостях, если A равно -1, а B равно 1, A+B=0верно, но A=0 and B=0неверно.

ГБН
источник
7

(Обратите внимание, что этот ответ был представлен до того, как какое-либо тестирование было отмечено в Вопросе: текст Вопроса заканчивался чуть выше раздела с результатами теста .)

Я бы предположил, что отдельные ANDусловия предпочтительнее, поскольку оптимизатор с большей вероятностью закорачивает операцию, если одно из них не равно 0, без необходимости сначала выполнять вычисления.

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

Соломон Руцкий
источник
3

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

В целом дизъюнкция (OR) работает хуже, чем конъюнкция (AND), но даже если у вас есть запрос с дизъюнкциями, я положу свои деньги на первый.

Леннарт
источник
2

Это простой вопрос

Нет это не так. Этот (своего рода) вопрос - то, что изводит многих администраторов баз данных и разработчиков программного обеспечения изо дня в день, и это почти тривиально.

что я не могу найти ответ для.

Да не будешь По крайней мере, не общий ответ. Прежде всего, это будет сильно зависеть от того, какую СУБД вы используете (хорошо, вы используете , но все же). Это может даже измениться, когда вы переходите от одной версии вашей RDBMS к другой.

Затем это может зависеть от любого количества других мелких деталей, например, как ваша БД хранит данные, если у вас есть подвыборы / объединения, которые путают проблему с оптимизатором плана и т. Д. Оптимизатор может предоставить вам разные планы выполнения в зависимости от на сколько строк у вас есть ...

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

Anoe
источник
0

Это может быть очевидно, но если столбцы равны INT, то a+b+cмогут равняться нулю, даже если ни один из них на самом деле не равен нулю. Вы тестируете две разные вещи!

Росс Прессер
источник
Просто понял, что @gbn упомянул об этом в своем ответе.
Росс Прессер,