Вы в основном уже ответили на вопрос сами. У меня есть несколько кусочков, чтобы добавить:
В PostgreSQL (и других СУБД, поддерживающих boolean
тип) вы можете boolean
напрямую использовать результат теста. Cast его integer
и SUM()
:
SUM((amount > 100)::int))
Или используйте это в NULLIF()
выражении и COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
Или с простым OR NULL
:
COUNT(amount > 100 OR NULL)
Или различные другие выражения. Производительность практически идентична . COUNT()
как правило, очень немного быстрее, чем SUM()
. В отличие от того, SUM()
что Павел уже прокомментировал , COUNT()
никогда не возвращается NULL
, что может быть удобно. Связанный:
Начиная с Postgres 9.4 есть также FILTER
пункт . Детали:
Это быстрее, чем все вышеперечисленное, примерно на 5-10%:
COUNT(*) FILTER (WHERE amount > 100)
Если запрос такой же простой, как ваш тестовый пример, с одним счетчиком и ничем иным, вы можете переписать:
SELECT count(*) FROM tbl WHERE amount > 100;
Который является истинным королем производительности, даже без индекса.
При использовании соответствующего индекса он может быть на порядок быстрее, особенно при сканировании только по индексу.
Ориентиры
Postgres 10
Я запустил новую серию тестов для Postgres 10, в том числе агрегатное FILTER
предложение и демонстрацию роли индекса для малых и больших количеств.
Простая настройка:
CREATE TABLE tbl (
tbl_id int
, amount int NOT NULL
);
INSERT INTO tbl
SELECT g, (random() * 150)::int
FROM generate_series (1, 1000000) g;
-- only relevant for the last test
CREATE INDEX ON tbl (amount);
Фактические времена меняются немного из-за фонового шума и особенностей испытательного стенда. Показаны типичные лучшие времена из большого набора тестов. Эти два случая должны охватывать суть:
Тест 1, подсчитывающий ~ 1% всех строк
SELECT COUNT(NULLIF(amount > 148, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 148)::int) FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount > 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount > 148 THEN 1 END) FROM tbl; -- 130 ms
SELECT COUNT((amount > 148) OR NULL) FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount > 148) FROM tbl; -- 118 ms -- !
SELECT count(*) FROM tbl WHERE amount > 148; -- without index -- 75 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 148; -- with index -- 1.4 ms -- !!!
дБ <> скрипка здесь
Тест 2 с подсчетом ~ 33% всех строк
SELECT COUNT(NULLIF(amount > 100, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 100)::int) FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount > 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount > 100 THEN 1 END) FROM tbl; -- 138 ms
SELECT COUNT(amount > 100 OR NULL) FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount > 100) FROM tbl; -- 132 ms -- !
SELECT count(*) FROM tbl WHERE amount > 100; -- without index -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 100; -- with index -- 55 ms -- !!!
дБ <> скрипка здесь
В последнем тесте в каждом наборе использовалось сканирование только по индексу , поэтому оно помогло подсчитать треть всех строк. Сканирования с обычным индексом или растровым индексом не могут конкурировать с последовательным сканированием, когда задействовано примерно 5% или более всех строк.
Старый тест для Postgres 9.1
Чтобы убедиться, что я провел быстрый тест EXPLAIN ANALYZE
на реальной таблице в PostgreSQL 9.1.6.
74208 из 184568 строк квалифицированы с условием kat_id > 50
. Все запросы возвращают одинаковый результат. Я запускал каждую из них по 10 раз по очереди, чтобы исключить эффекты кэширования, и добавил в качестве примечания лучший результат:
SELECT SUM((kat_id > 50)::int) FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id > 50, FALSE)) FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id > 50 THEN 1 END) FROM log_kat; -- 437 ms
SELECT COUNT((kat_id > 50) OR NULL) FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id > 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms
Вряд ли какая-то реальная разница в производительности.
FILTER
чем с выражениями выше (тестирование с помощью pg 9.5). Вы получаете то же самое? (WHERE
по-прежнему царь производительности - где это возможно).FILTER
Решение является , как правило , быстрее в моих тестах.Это мой тест на SQL Server 2012 RTM.
Глядя на отдельные партии и партии отдельно
Результаты после запуска 5 раз (и повторения) довольно неубедительны.
Это показывает, что в условиях работы наблюдается гораздо большая изменчивость, чем разница между реализацией, если измерять ее с помощью детализации таймера SQL Server. Любая версия может быть лучшей, и максимальная дисперсия, которую я когда-либо получал, составляет 2,5%.
Тем не менее, принимая другой подход:
StmtText (SUM)
StmtText (COUNT)
Из моего чтения, кажется, что версия SUM делает немного больше. Он выполняет COUNT в дополнение к SUM. Сказав это,
COUNT(*)
он отличается и должен быть быстрее, чемCOUNT([Expr1004])
(пропустите NULL, больше логики). Разумный оптимизатор поймет , что[Expr1004]
вSUM([Expr1004])
в версии SUM является типом «INT» и поэтому использовать целочисленный регистр.В любом случае, хотя я все еще верю, что
COUNT
в большинстве СУБД версия будет быстрее, мой вывод из тестирования таков, что я собираюсь продолжитьSUM(.. 1.. 0..)
в будущем, по крайней мере для SQL Server, не по другой причине, кроме предупреждений ANSI, возникающих при использованииCOUNT
,источник
По моему опыту Как сделать трассировку, для обоих методов в запросе около 10 000 000 я заметил, что Count (*) использует примерно вдвое больше ЦП и работает немного быстрее. но мои Запросы без фильтра.
Count (*)
Сумма (1)
источник