Для этого точного запроса, не совсем; выполнение этой операции требует сканирования каждой строки. Обойти это невозможно.
Но если вы хотите быстро получить набор уникальных категорий и у вас есть индекс для этого столбца, вы можете использовать вариант примера WITH RECURSIVE
, показанный здесь при редактировании вопроса ( посмотрите в конец вопроса):
Подсчет отдельных строк с использованием рекурсивного cte по непонятному индексу
Вам нужно будет изменить его, чтобы он возвращал набор уникальных значений вместо их подсчета, но это выглядит как простое изменение:
testdb=# create table tb(id bigserial, category smallint);
CREATE TABLE
testdb=# insert into tb(category) select 2 from generate_series(1, 10000)
testdb-# ;
INSERT 0 10000
testdb=# insert into tb(category) select 1 from generate_series(1, 10000);
INSERT 0 10000
testdb=# insert into tb(category) select 3 from generate_series(1, 10000);
INSERT 0 10000
testdb=# create index on tb(category);
CREATE INDEX
testdb=# WITH RECURSIVE cte AS
(
(SELECT category
FROM tb
WHERE category >= 0
ORDER BY 1
LIMIT 1)
UNION ALL SELECT
(SELECT category
FROM tb
WHERE category > c.category
ORDER BY 1
LIMIT 1)
FROM cte c
WHERE category IS NOT NULL)
SELECT category
FROM cte
WHERE category IS NOT NULL;
category
----------
1
2
3
(3 rows)
А вот и EXPLAIN ANALYZE
:
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
CTE Scan on cte (cost=40.66..42.68 rows=100 width=2) (actual time=0.057..0.127 rows=3 loops=1)
Filter: (category IS NOT NULL)
Rows Removed by Filter: 1
CTE cte
-> Recursive Union (cost=0.29..40.66 rows=101 width=2) (actual time=0.052..0.119 rows=4 loops=1)
-> Limit (cost=0.29..0.33 rows=1 width=2) (actual time=0.051..0.051 rows=1 loops=1)
-> Index Only Scan using tb_category_idx on tb tb_1 (cost=0.29..1363.29 rows=30000 width=2) (actual time=0.050..0.050 rows=1 loops=1)
Index Cond: (category >= 0)
Heap Fetches: 1
-> WorkTable Scan on cte c (cost=0.00..3.83 rows=10 width=2) (actual time=0.015..0.015 rows=1 loops=4)
Filter: (category IS NOT NULL)
Rows Removed by Filter: 0
SubPlan 1
-> Limit (cost=0.29..0.36 rows=1 width=2) (actual time=0.016..0.016 rows=1 loops=3)
-> Index Only Scan using tb_category_idx on tb (cost=0.29..755.95 rows=10000 width=2) (actual time=0.015..0.015 rows=1 loops=3)
Index Cond: (category > c.category)
Heap Fetches: 2
Planning time: 0.224 ms
Execution time: 0.191 ms
(19 rows)
Количество циклов, которое он должен выполнить для узла сканирования WorkTable
, будет равно количеству уникальных категорий, которые у вас есть плюс одна, поэтому он должен оставаться очень быстрым, скажем, до сотен уникальных значений.
Другой способ, которым вы можете воспользоваться, - это добавить еще одну таблицу, в которой вы просто храните уникальные значения tb.category
, и логика приложения проверяет эту таблицу и вставляет их значение при обновлении / вставке этого столбца. Это также может быть сделано на стороне базы данных с помощью триггеров; это решение также обсуждается в ответах на связанный вопрос.