# PostgreSQL Function Volatility Categories
* [官方文檔](https://www.postgresql.org/docs/current/xfunc-volatility.html)
會注意到這件事是先前用 bigint 型別的 unixtimestamp 當 partition key 欄位,發現若在查詢條件做時間轉換,有查詢計劃分析不佳而全表查詢的情況。
可將此時間轉換部分包裝為函數 `to_unixtimestamp`,並將此函數設定為 `IMMUTABLE` 類別,此類別的函數在 PostgreSQL 查詢計畫中會被預先運算。
## Function Volatility Categories
### VOLATILE 易變的
PostgreSQL Function 預設類型。
此類 Function 會被 PostgreSQL 假設為「可能會修改資料庫」或「可能返回不確定的結果」,故 PostgreSQL 查詢器對此 Function 的優化會較保守,並且在每次查詢執行時都重新評估。
:::info
建立 temp table 也算是修改資料庫的行為,但 cte 不算。
:::
### STABLE 穩定的
此類 Function 不能修改資料庫,且在「同一個查詢」中相同參數會返回一樣的結果。
舉例來說,以下這個查詢,兩次調用 `stable_function`,若輸入的參數相同則必定返回相同結果。
```sql
SELECT
stable_function(column1),
stable_function(column1)
FROM some_table;
```
而以下這個查詢,兩次調用 `stable_function` 是在不同查詢,輸入相同參數卻返回不同結果是允許的。
```sql
SELECT stable_function(column1) FROM table1;
SELECT stable_function(column1) FROM table1;
```
PostgreSQL 查詢器在同一個查詢中會重複使用函數結果,故須確保 Function 在同一個查詢中的結果一致性。
相較 `VOLATILE` 可提高查詢性能。
### IMMUTABLE 不可變的
此類 Function 不能修改資料庫,且相同的輸入參數「永遠」返回一樣的結果,也就是說 Function 結果完全依賴所輸入的參數。
適合純數學計算、不依賴資料庫內容的邏輯。
PostgreSQL 查詢器在進行規劃前就會先預先計算好此 Function 的結果,以產出更精確的查詢計劃。
## Demo
準備測試資料。
```sql
-- create partitioned table
create table public.bigint_partition_table (
id uuid default gen_random_uuid(),
content varchar not null,
created_at bigint default round(extract(epoch from now())*1000),
primary key (id, created_at)
) partition by range (created_at);
-- create partition by pg_partman
SELECT partman.create_parent(
p_parent_table := 'public.bigint_partition_table' -- 父表
, p_control := 'created_at' -- Partition Key
, p_interval := '1 day' -- 每個分區的時間長度
, p_type := 'range' -- 支援 range/list partition
, p_epoch := 'milliseconds' -- 若 p_control 是 integer/bigint 欄位,透過此參數設定該數字所代表的意義 (seconds/milliseconds/microseconds/nanoseconds)
, p_premake := 7 -- 領先於當前時間的分區數
, p_start_partition := (current_date-30)::varchar -- 起始分區的時間/數值
, p_default_table := true -- 是否建立 Default Partition
, p_automatic_maintenance := 'on' -- 是否啟動自動維護
-- , p_constraint_cols := NULL -- 約束條件
-- , p_template_table := NULL -- 模板表
, p_jobmon := false -- 是否透過 pg_jobmon 監控 pg_partman 的工作 (我沒安裝故設定 false)
-- , p_date_trunc_interval := NULL -- Partition 時間截斷方式
, p_control_not_null := true -- 是否 partition key 不能為 NULL
-- , p_time_encoder := NULL -- 若 Partition Key 是非標準的時間格式或非時間類型,則需在此參數提供將 Partition Key 轉換為時間格式的函數
-- , p_time_decoder := NULL -- 若 Partition Key 是非標準的時間格式或非時間類型,則需在此參數提供將時間格式轉換為 Partition Key 的函數
);
INSERT INTO public.bigint_partition_table(content, created_at)
SELECT
md5(random()::text) AS content,
1746633600000::bigint + (2678399999 * random())::bigint AS created_at
FROM generate_series(1, 1000000);
```
直接在 `WHERE` 條件做時間轉換。
```sql
EXPLAIN ANALYZE
SELECT
content
FROM public.bigint_partition_table
WHERE created_at
BETWEEN round(extract(epoch from current_date - 10)*1000)
AND round(extract(epoch from now())*1000);
```
查詢計劃如下,可以看到是把所有分區都翻出來搜尋了,Partition Key 等於沒用。
:::spoiler
```text
"Gather (cost=1000.00..38162.01 rows=5040 width=33) (actual time=43.971..146.031 rows=331513 loops=1)"
" Workers Planned: 5"
" Workers Launched: 5"
" -> Parallel Append (cost=0.00..36658.01 rows=1003 width=33) (actual time=35.442..103.826 rows=55252 loops=6)"
" -> Parallel Seq Scan on bigint_partition_table_p20250607 bigint_partition_table_31 (cost=0.00..1188.80 rows=96 width=33) (actual time=0.716..24.754 rows=19660 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 13012"
" -> Parallel Seq Scan on bigint_partition_table_p20250531 bigint_partition_table_24 (cost=0.00..1186.28 rows=96 width=33) (actual time=0.470..26.978 rows=32611 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250520 bigint_partition_table_13 (cost=0.00..1183.00 rows=96 width=33) (actual time=50.027..50.027 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32520"
" -> Parallel Seq Scan on bigint_partition_table_p20250518 bigint_partition_table_11 (cost=0.00..1182.45 rows=95 width=33) (actual time=40.457..40.457 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32498"
" -> Parallel Seq Scan on bigint_partition_table_p20250530 bigint_partition_table_23 (cost=0.00..1180.62 rows=95 width=33) (actual time=0.074..23.142 rows=32465 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250516 bigint_partition_table_9 (cost=0.00..1180.40 rows=95 width=33) (actual time=16.851..16.851 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32456"
" -> Parallel Seq Scan on bigint_partition_table_p20250601 bigint_partition_table_25 (cost=0.00..1179.35 rows=95 width=33) (actual time=0.017..18.888 rows=32414 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250528 bigint_partition_table_21 (cost=0.00..1178.62 rows=95 width=33) (actual time=0.028..19.472 rows=21549 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 10836"
" -> Parallel Seq Scan on bigint_partition_table_p20250602 bigint_partition_table_26 (cost=0.00..1177.43 rows=95 width=33) (actual time=0.020..21.165 rows=32377 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250514 bigint_partition_table_7 (cost=0.00..1177.10 rows=95 width=33) (actual time=14.787..14.787 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32364"
" -> Parallel Seq Scan on bigint_partition_table_p20250524 bigint_partition_table_17 (cost=0.00..1176.47 rows=95 width=33) (actual time=13.843..13.843 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32339"
" -> Parallel Seq Scan on bigint_partition_table_p20250511 bigint_partition_table_4 (cost=0.00..1175.93 rows=95 width=33) (actual time=16.388..16.388 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32317"
" -> Parallel Seq Scan on bigint_partition_table_p20250517 bigint_partition_table_10 (cost=0.00..1174.28 rows=95 width=33) (actual time=17.523..17.523 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32291"
" -> Parallel Seq Scan on bigint_partition_table_p20250509 bigint_partition_table_2 (cost=0.00..1173.73 rows=95 width=33) (actual time=20.752..20.752 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32269"
" -> Parallel Seq Scan on bigint_partition_table_p20250519 bigint_partition_table_12 (cost=0.00..1173.28 rows=95 width=33) (actual time=22.864..22.864 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32251"
" -> Parallel Seq Scan on bigint_partition_table_p20250515 bigint_partition_table_8 (cost=0.00..1173.00 rows=95 width=33) (actual time=15.592..15.592 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32240"
" -> Parallel Seq Scan on bigint_partition_table_p20250527 bigint_partition_table_20 (cost=0.00..1172.70 rows=95 width=33) (actual time=13.461..13.462 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32228"
" -> Parallel Seq Scan on bigint_partition_table_p20250512 bigint_partition_table_5 (cost=0.00..1172.60 rows=95 width=33) (actual time=13.713..13.713 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32224"
" -> Parallel Seq Scan on bigint_partition_table_p20250522 bigint_partition_table_15 (cost=0.00..1170.60 rows=95 width=33) (actual time=12.981..12.981 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32184"
" -> Parallel Seq Scan on bigint_partition_table_p20250603 bigint_partition_table_27 (cost=0.00..1170.00 rows=95 width=33) (actual time=0.007..8.273 rows=16080 loops=2)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250526 bigint_partition_table_19 (cost=0.00..1169.80 rows=95 width=33) (actual time=15.864..15.864 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32152"
" -> Parallel Seq Scan on bigint_partition_table_p20250606 bigint_partition_table_30 (cost=0.00..1169.65 rows=95 width=33) (actual time=0.009..5.827 rows=10715 loops=3)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250510 bigint_partition_table_3 (cost=0.00..1169.10 rows=95 width=33) (actual time=10.662..10.662 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32124"
" -> Parallel Seq Scan on bigint_partition_table_p20250604 bigint_partition_table_28 (cost=0.00..1169.03 rows=95 width=33) (actual time=0.013..2.614 rows=5354 loops=6)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250521 bigint_partition_table_14 (cost=0.00..1167.80 rows=95 width=33) (actual time=5.453..5.454 rows=0 loops=2)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 16056"
" -> Parallel Seq Scan on bigint_partition_table_p20250508 bigint_partition_table_1 (cost=0.00..1166.97 rows=94 width=33) (actual time=4.938..4.938 rows=0 loops=2)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 16040"
" -> Parallel Seq Scan on bigint_partition_table_p20250525 bigint_partition_table_18 (cost=0.00..1165.85 rows=94 width=33) (actual time=10.518..10.518 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32034"
" -> Parallel Seq Scan on bigint_partition_table_p20250513 bigint_partition_table_6 (cost=0.00..1164.75 rows=94 width=33) (actual time=13.020..13.020 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 32030"
" -> Parallel Seq Scan on bigint_partition_table_p20250605 bigint_partition_table_29 (cost=0.00..1164.18 rows=94 width=33) (actual time=0.011..17.602 rows=32007 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250529 bigint_partition_table_22 (cost=0.00..1164.08 rows=94 width=33) (actual time=0.009..17.338 rows=32003 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250523 bigint_partition_table_16 (cost=0.00..1161.18 rows=94 width=33) (actual time=39.968..39.968 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" Rows Removed by Filter: 31927"
" -> Parallel Seq Scan on bigint_partition_table_p20250608 bigint_partition_table_32 (cost=0.00..34.25 rows=3 width=32) (actual time=0.001..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250609 bigint_partition_table_33 (cost=0.00..34.25 rows=3 width=32) (actual time=0.000..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250610 bigint_partition_table_34 (cost=0.00..34.25 rows=3 width=32) (actual time=0.001..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250611 bigint_partition_table_35 (cost=0.00..34.25 rows=3 width=32) (actual time=0.001..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250612 bigint_partition_table_36 (cost=0.00..34.25 rows=3 width=32) (actual time=0.000..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250613 bigint_partition_table_37 (cost=0.00..34.25 rows=3 width=32) (actual time=0.001..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_p20250614 bigint_partition_table_38 (cost=0.00..34.25 rows=3 width=32) (actual time=0.001..0.001 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
" -> Parallel Seq Scan on bigint_partition_table_default bigint_partition_table_39 (cost=0.00..34.25 rows=3 width=32) (actual time=0.004..0.004 rows=0 loops=1)"
" Filter: (((created_at)::numeric <= round((EXTRACT(epoch FROM now()) * '1000'::numeric), 0)) AND ((created_at)::numeric >= round((EXTRACT(epoch FROM (CURRENT_DATE - 10)) * '1000'::numeric), 0)))"
"Planning Time: 5.165 ms"
"Execution Time: 158.593 ms"
```
:::
測試把轉換時間的部分包成 `IMMUTABLE` 函數。
```sql
CREATE OR REPLACE FUNCTION public.to_unixtimestamp(timestamp with time zone)
RETURNS bigint
LANGUAGE 'plpgsql'
IMMUTABLE
AS $BODY$
BEGIN
RETURN round(extract(epoch from $1) * 1000);
END;
$BODY$;
```
```sql
EXPLAIN ANALYZE
SELECT
content
FROM public.bigint_partition_table
WHERE created_at
BETWEEN to_unixtimestamp(current_date-10)
AND to_unixtimestamp(now());
```
查詢計劃如下,變成精準查詢指定分區了。
:::spoiler
```text
"Append (cost=0.80..27027.21 rows=341694 width=33) (actual time=0.038..128.206 rows=341724 loops=1)"
" Subplans Removed: 28"
" -> Index Scan using bigint_partition_table_p20250528_pkey on bigint_partition_table_p20250528 bigint_partition_table_1 (cost=0.80..1290.84 rows=32379 width=33) (actual time=0.037..17.515 rows=32385 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250529_pkey on bigint_partition_table_p20250529 bigint_partition_table_2 (cost=0.80..1268.90 rows=31997 width=33) (actual time=0.071..13.010 rows=32003 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250530_pkey on bigint_partition_table_p20250530 bigint_partition_table_3 (cost=0.80..1295.73 rows=32459 width=33) (actual time=0.030..9.589 rows=32465 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250531_pkey on bigint_partition_table_p20250531 bigint_partition_table_4 (cost=0.80..1312.95 rows=32604 width=33) (actual time=0.031..8.208 rows=32611 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250601_pkey on bigint_partition_table_p20250601 bigint_partition_table_5 (cost=0.80..1294.72 rows=32408 width=33) (actual time=0.026..23.642 rows=32414 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250602_pkey on bigint_partition_table_p20250602 bigint_partition_table_6 (cost=0.80..1286.28 rows=32371 width=33) (actual time=0.051..8.158 rows=32377 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250603_pkey on bigint_partition_table_p20250603 bigint_partition_table_7 (cost=0.80..1279.74 rows=32154 width=33) (actual time=0.037..6.889 rows=32160 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250604_pkey on bigint_partition_table_p20250604 bigint_partition_table_8 (cost=0.80..1266.86 rows=32115 width=33) (actual time=0.026..6.955 rows=32121 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250605_pkey on bigint_partition_table_p20250605 bigint_partition_table_9 (cost=0.80..1277.78 rows=32001 width=33) (actual time=0.030..6.942 rows=32007 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250606_pkey on bigint_partition_table_p20250606 bigint_partition_table_10 (cost=0.80..1280.56 rows=32140 width=33) (actual time=0.027..7.296 rows=32146 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
" -> Index Scan using bigint_partition_table_p20250607_pkey on bigint_partition_table_p20250607 bigint_partition_table_11 (cost=0.80..1163.28 rows=19006 width=33) (actual time=0.031..5.275 rows=19035 loops=1)"
" Index Cond: ((created_at >= to_unixtimestamp(((CURRENT_DATE - 10))::timestamp with time zone)) AND (created_at <= to_unixtimestamp(now())))"
"Planning Time: 1.881 ms"
"Execution Time: 135.339 ms"
```
:::