Антипаттерн 'WHERE <что-то> IS NOT true'
Жила-была большая-пребольшая база, и была в ней таблица на 300 миллионами записей. Хранились в таблице складские данные,
и был в ней столбец deleted
, в котором помечалось, если товар со склада перемещался в магазин. И графана с забиксом были
красные-красные, потому что приходил в базу запрос “а дай-ка мне товары, которые не перемещены в магазин”, в котором было
условие WHERE deleted IS NOT true
.
Попробуем провести эксперимент.
Создадим таблицу из двух колонок и признака, удалена запись или нет
CREATE TABLE IF NOT EXISTS public.some_table
(
column1 text COLLATE pg_catalog."default" NOT NULL,
column2 integer NOT NULL DEFAULT 0,
deleted boolean NOT NULL
);
CREATE INDEX IF NOT EXISTS deleted_index
ON public.some_table USING btree
(deleted ASC NULLS LAST);
Сгенерируем 10 тысяч значений
INSERT INTO some_table (deleted, column1, column2)
SELECT true, 'x', s.column2
FROM generate_series(1, 10000) AS s (column2);
Случайным образом выставим признак deleted
UPDATE some_table
SET deleted = random() > 0.5;
А одинаковый ли результат будет, проверим ?
# SELECT COUNT(*) FROM some_table
# WHERE deleted is not true
# UNION ALL
# SELECT COUNT(*) FROM some_table
# WHERE deleted is false;
count
-------
4995
4995
(2 rows)
А что с планами запросов ?
# EXPLAIN ANALYZE SELECT COUNT(*) FROM some_table
# WHERE deleted is not true;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Aggregate (cost=221.49..221.50 rows=1 width=8) (actual time=0.967..0.968 rows=1 loops=1)
-> Seq Scan on some_table (cost=0.00..209.00 rows=4995 width=0) (actual time=0.072..0.811 rows=4995 loops=1)
Filter: (deleted IS NOT TRUE)
Rows Removed by Filter: 5005
Planning Time: 0.105 ms
Execution Time: 0.995 ms
(6 rows)
# EXPLAIN ANALYZE SELECT COUNT(*) FROM some_table
# WHERE deleted is false;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Aggregate (cost=136.19..136.19 rows=1 width=8) (actual time=0.603..0.603 rows=1 loops=1)
-> Index Only Scan using deleted_index on some_table (cost=0.29..123.70 rows=4995 width=0) (actual time=0.025..0.391 rows=4995 loops=1)
Index Cond: (deleted = false)
Heap Fetches: 0
Planning Time: 0.091 ms
Execution Time: 0.625 ms
(6 rows)
#
Вывод
`WHERE <что-то> IS NOT true" - плохо
"WHERE <что-то> IS false" - хорошо
(но надо не забыть создать индекс)что-то>что-то>
Как можно найти такие запросы
Например, при помощи pg_stat_statements:
SELECT calls, round(total_time/calls) "average", round(total_exec_time), query
FROM pg_stat_statements
WHERE lower(query) like '%is not true%'
ORDER BY total_exec_time desc