#sql #postgresql #window-functions #ranking #ranking-functions
Вопрос:
Таблица Dok содержит номера заказов :
create table dok ( doktyyp char(1),
tasudok char(25) );
CREATE INDEX dok_tasudok_idx ON dok (tasudok);
CREATE UNIQUE INDEX dok_tasudok_unique_idx ON dok (doktyyp,tasudok)
WHERE doktyyp IN ( 'T', 'U') ;
Номера заказов содержат одну и ту же начальную часть и достаточно разных, таких как
91000465663
91000465663-1
91000465663-2
91000465663-T
91000465663-T-1
Как создать запрос, который возвращает порядковый номер заказа по заданному префиксу. Для столбца doktyyp значение всегда является постоянным «T».
Для приведенных ниже цифр результаты должны быть
91000465663 returns 1
91000465663-1 returns 2
91000465663-2 returns 3
91000465663-T returns 4
91000465663-T-1 returns 5
Запрос
with koik as (
select rank() over (order by tasudok), tasudok
from dok
where doktyyp='T' and tasudok like '91000465663%'
)
select rank
from koik
where tasudok='91000465663-1'
Кажется, работает правильно, но выглядит слишком долго для этой простой задачи. Как создать более короткий и качественный запрос ?
Объясните вывод:
"Subquery Scan on koik (cost=685.04..685.07 rows=1 width=8)"
" Filter: (koik.tasudok = '91000465663-1'::bpchar)"
" -> WindowAgg (cost=685.04..685.06 rows=1 width=34)"
" -> Sort (cost=685.04..685.05 rows=1 width=26)"
" Sort Key: dok.tasudok"
" -> Bitmap Heap Scan on dok (cost=23.55..685.03 rows=1 width=26)"
" Recheck Cond: (doktyyp = 'T'::bpchar)"
" Filter: (tasudok ~~ '91000465663%'::text)"
" -> Bitmap Index Scan on dok_tasudok_unique_idx (cost=0.00..23.55 rows=437 width=0)"
" Index Cond: (doktyyp = 'T'::bpchar)"
Использование postgres 11
Комментарии:
1. есть ли индекс по столбцу тасудок? Что говорит ОБЪЯСНЕНИЕ?
2. Я обновил вопрос и добавил индексы и объяснил вывод
Ответ №1:
Если я правильно понял ваш вариант использования, в запросе всегда будет учитываться фиксированный префикс первых 11 символов tasudok
, поэтому для ускорения работы вы можете указать индексу, чтобы tasudok
он уже был усечен до первых 11 символов (частичный индекс), чтобы вам не нужно было использовать like.
CREATE INDEX dok_doktyyp_tasudok_idx ON dok (doktyyp, SUBSTRING(tasudok FROM 1 FOR 11));
И запрос должен выглядеть так
WITH koik AS (
SELECT RANK() OVER (ORDER BY tasudok), tasudok
FROM dok
WHERE doktyyp='T' AND SUBSTRING(tasudok FROM 1 FOR 11) = '91000465663'
)
SELECT tasudok,rank FROM koik
WHERE tasudok = '91000465663-1';
Конечно, это не имело бы смысла, если бы размер префикса был переменным.
ДЕМОНСТРАЦИЯ: db<>fiddle