I'm trying to optimise a query to retrieve records across multiple days on a partitioned table (say: entries). The table is partitioned on a timestamp column (let's say: created_at) and for each day a new table is created due to partition.
The schema of the table
Partitioned table "public.entries"
Column | Type | Collation | Nullable |
-----------------------+-----------------------------+-----------+----------+-
id | character varying(48) | | not null |
person_id | bigint | | not null |
created_at | timestamp without time zone | | not null |
created_at_date | date | | |
created_at_time | time without time zone | | |
Partition key: RANGE (created_at)
Indexes:
"entries_pkey" PRIMARY KEY, btree (id, created_at)
"person_id_created_at_key" UNIQUE CONSTRAINT, btree (person_id, created_at)
"btree_gist_created_at" gist (person_id, tsrange(created_at, created_at, '[]'::text))
"person_id_cd_ct_idx" btree (person_id, created_at_date, created_at_time)
"person_id_created_at_idx" btree (person_id, (created_at::date), (created_at::time without time zone))
Number of partitions: 30 (Use \d+ to list them.)
I need to fetch all the possible results for a particular person who has done entries between a certain period of days (say: 2022-06-01 to 2022-06-05, from 08:00 to 20:00). Each partitioned table has around 3 million rows, meaning we have ~3 million daily entries. As soon as I increase the range of the period of days, my query time increases, which becomes a problem for me when I need to cater for getting results for 30 days.
I also have tried adding different types of indexes on person_id
& created_at
but still, I'm unable to query much quicker than I need it to be. I need to get the results in a single-digit ms (~5ms - ~9ms) (if that is even possible). Currently, for the mentioned queries, the times I'm getting are ~20ms to ~100ms considering all of them, but I want it to be in single-digits.
The following queries are taking more than ~100ms if I try to query over 30 days of records. I need to optimise these queries for 30 days, though the queries mentioned in the example are for 5-6 days only for the sake of it.
I'm trying to use the following queries to fetch the results:
-- QUERY #1 (Most efficient)
SELECT person_id, created_at
FROM entries
WHERE person_id = '111111'
AND (
(
created_at >= '2022-06-01 08:00:00'
AND created_at <= '2022-06-01 20:00:00'
) OR (
created_at >= '2022-06-02 08:00:00'
AND created_at <= '2022-06-02 20:00:00'
) OR (
created_at >= '2022-06-03 08:00:00'
AND created_at <= '2022-06-03 20:00:00'
) OR (
created_at >= '2022-06-04 08:00:00'
AND created_at <= '2022-06-04 20:00:00'
) OR (
created_at >= '2022-06-05 08:00:00'
AND created_at <= '2022-06-05 20:00:00'
)
);
-- QUERY #2 (2nd most efficient)
SELECT person_id, created_at
FROM entries
WHERE person_id = '111111'
AND created_at_date >= '2022-06-01'
AND created_at_date <= '2022-06-05'
AND created_at_time >= '08:00:00'
AND created_at_time <= '20:00:00';
-- QUERY #3 (Least efficient)
SELECT person_id, created_at
FROM entries
WHERE person_id = '111111'
AND (
'[2022-06-01 08:00:00, 2022-06-01 20:00:00]'::tsrange @> created_at
OR '[2022-06-02 08:00:00, 2022-06-02 20:00:00]'::tsrange @> created_at
OR '[2022-06-03 08:00:00, 2022-06-03 20:00:00]'::tsrange @> created_at
OR '[2022-06-04 08:00:00, 2022-06-04 20:00:00]'::tsrange @> created_at
OR '[2022-06-05 08:00:00, 2022-06-05 20:00:00]'::tsrange @> created_at
);
I'm sharing the query plans for the above-mentioned queries:
Can you suggest to me if is there any way I can make the query more efficient either by changing the structure of the table or by using some different sort of index combinations? Is there any other possibility or the way I'm querying my DB is the most efficient query I can do depending on my scenario?
EXPLAIN (ANALYZE, BUFFERS)
and with track_io_timing turned on.SELECT * ...
cannot possibly generateIndex Only Scan using entries_p2022_06_05_person_id_created_at_key
like seen in the first query plan. You must have been running a different query, likeSELECT person_id, created_at ...
. Does not change my answer, but such inaccuracies can easily lead us on a wild goose chase, so don't do that, please.