And*_*sov 5 postgresql index datatypes postgresql-performance
我的表包含列的索引total_balance:
\d balances_snapshots
Table "public.balances_snapshots"
Column | Type | Collation | Nullable | Default
---------------+-----------------------------+-----------+----------+------------------------------------------------
user_id | integer | | |
asset_id | text | | |
timestamp | timestamp without time zone | | | now()
total_balance | numeric | | not null |
id | integer | | not null | nextval('balances_snapshots_id_seq'::regclass)
Indexes:
"balances_snapshots_pkey" PRIMARY KEY, btree (id)
"balances_snapshots_asset_id_idx" btree (asset_id)
"balances_snapshots_timestamp_idx" btree ("timestamp")
"balances_snapshots_user_id_idx" btree (user_id)
"balances_total_balance_idx" btree (total_balance)
Foreign-key constraints:
"balances_snapshots_asset_id_fkey" FOREIGN KEY (asset_id) REFERENCES assets(id) ON UPDATE CASCADE ON DELETE CASCADE
"balances_snapshots_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
Run Code Online (Sandbox Code Playgroud)
简单的查询适用于 seq 扫描
explain analyze SELECT EXISTS (
SELECT
1
FROM
balances_snapshots
WHERE
total_balance = double precision 'NaN'
LIMIT 1
) as exists;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
Result (cost=4.75..4.76 rows=1 width=1) (actual time=237365.680..237365.681 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Seq Scan on balances_snapshots (cost=0.00..9257326.32 rows=1948181 width=0) (actual time=237365.675..237365.676 rows=0 loops=1)
Filter: ((total_balance)::double precision = 'NaN'::double precision)
Rows Removed by Filter: 389636289
Planning Time: 23.985 ms
Execution Time: 237365.719 ms
(7 rows)
Run Code Online (Sandbox Code Playgroud)
如何让 PostgreSQL 使用索引?或者换句话说,是否有更有效的方法来扫描存在 NaN 值的表?
Sta*_*sev 10
问题是 PG 必须将原始数字转换为双精度:
Filter: ((total_balance)::double precision = ...
Run Code Online (Sandbox Code Playgroud)
因此,构建的索引包含数字,但您需要双精度 - 因此无法使用索引。您需要避免转换列中的值:
WHERE
total_balance = 'NaN'::numeric
Run Code Online (Sandbox Code Playgroud)
PS:在数据库中看到 NaN 很奇怪。为什么不存储空值?
Postgres 有一个 forfloat8 = float8和 for运算符numeric = numeric。但不适合numeric = float8。必须强制转换一个操作数。
double precision,又名float8是数字类型中的“首选”数据类型。请参阅pg_type.typispreferred。
运算符类型解析最终在第 3.d段中决定。
遍历所有候选者并保留接受首选类型的候选者
float8 = float8获胜。您的索引是基于运算符类构建的,numeric并且不适用。砰。
double precision将查询中的强制转换替换为更合理的强制转换,匹配Stanislav 已经建议的numeric类型。total_balance
或者只是使用无类型文字'NaN'而不进行显式强制转换。total_balance这会自动解析为作业中的类型。看:
是否有更有效的方法来扫描存在 NaN 值的表?
如果这是您查询的重点,那么部分索引对于您的情况会更有效:
CREATE INDEX balances_total_balance_nan_idx ON balances_snapshots ((true))
WHERE total_balance = 'NaN';
Run Code Online (Sandbox Code Playgroud)
实际的索引表达式并不重要。我用了一个常数。看:
你的EXPLAIN输出报告接近 4 亿行 ( 389636289),但实际上没有一行具有total_balance = 'NaN'( rows=0)。建议的部分索引的最小大小为 8 kB,而不是完整索引的约 10 GB(?),几乎没有任何写入成本,并且使查询几乎可以即时运行。
(您是否还需要所有其他现有索引?)
中的表达式为WHERE必须与查询中使用的表达式匹配。如果查询中奇怪的类型不匹配有充分的理由(?),请执行以下操作:
...
WHERE total_balance = float8 'NaN'
Run Code Online (Sandbox Code Playgroud)
说到这里,像这样对表列重新排序将节省相当多 MB 的存储空间和 RAM:
Table "public.balances_snapshots"
Column | Type | Collation | Nullable | Default
---------------+-----------------------------+-----------+----------+------------------------------------------------
id | integer | | not null | nextval('balances_snapshots_id_seq'::regclass)
user_id | integer | | |
total_balance | numeric | | not null |
asset_id | text | | |
timestamp | timestamp without time zone | | | now()
Run Code Online (Sandbox Code Playgroud)
看:
| 归档时间: |
|
| 查看次数: |
1603 次 |
| 最近记录: |