我有一个带有数字字段的表,比如说:
create table data (
id bigserial PRIMARY KEY,
quantity numeric(21,8) NOT NULL
)
我需要一个numeric
类型,因为有些查询需要一定程度的准确度,而这些准确度无法从双打中获得。
但我也有一些查询可以累计数百万这些数量,不关心舍入问题,需要尽可能快。
是否有标准策略可以执行此操作,或者我应该只复制每个数字:
create table data (
id bigserial PRIMARY KEY,
quantity_exact numeric(21,8) NOT NULL,
quantity double precision NOT NULL
)
答案 0 :(得分:2)
请阅读以下更新以获取完整视图。
让我们比较一下你概述的两种情况:
float8
或numeric(21,8)
?)或keep two
)。一些观察结果。
如果同时保留这两列,我们会说数据重复,这与规范化相矛盾,并引入系统的模糊性,需要特殊处理。这使-1
成为keep two
。
列的大小为:
SELECT 'float8'::coltyp, pg_column_size(random()::float8) UNION ALL
SELECT 'numeric(21,8)', pg_column_size(random()::numeric(21,8));
在这种情况下保留两列将需要几乎两倍的空间。 -1
至keep two
案例以及+0.5
变体float8
,因为它的尺寸略小。
速度测试显示以下内容:
SET work_mem TO '2000MB'; -- to avoid usage of temp files
EXPLAIN (analyze,buffers,verbose)
SELECT ((random()*1234567)::float8 / 2 + 3) * 5
FROM generate_series(1,(1e7)::int) s;
EXPLAIN (analyze,buffers,verbose)
SELECT ((random()*1234567)::numeric(21,8) / 2 + 3) * 5
FROM generate_series(1,(1e7)::int) s;
在我的i7 2.3GHz MBP上,我得到了(基于5次运行):
3135.238ms
和float8
17325.514ms
以后不再是numeric(21,8)
。所以我们在+1
案例中明确float8
。这是一个仅限内存的测试,查询一个表(和一个冷表)将需要更多的时间。
考虑到您的性能要求,似乎坚持使用float8
是一种明显的方法(+1.5
vs -2
)。您可以在此表格的顶部创建一个视图,该视图将同时宣传原始float8
和已投放numeric(21,8)
,以满足您对高精度要求的查询。
更新:在a_horse_with_no_name
发表评论后,我决定重新测试,这次是使用真实表格。我去了9.4beta3,因为9.4带有非常好的pg_prewarm
模块。
这就是我所做的:
export PGDATA=$HOME/9.4b3
initdb -k -E UTF8 -A peer
pg_ctl start
然后,我使用新的ALTER SYSTEM
功能更改了一些默认设置:
ALTER SYSTEM SET shared_buffers TO '1280MB';
ALTER SYSTEM SET checkpoint_segments TO '99';
ALTER SYSTEM SET checkpoint_completion_target TO '0.9';
通过pg_ctl restart
重新启动服务器,现在是测试:
SELECT id::int, 1::int AS const, (random()*1234567)::float8 as val
INTO f FROM generate_series(1,(1e7)::int) id;
SELECT id::int, 1::int AS const, (random()*1234567)::numeric(21,8) as val
INTO n FROM generate_series(1,(1e7)::int) id;
CREATE EXTENSION pg_prewarm;
VACUUM ANALYZE;
SELECT pg_prewarm('f');
SELECT pg_prewarm('n');
-- checking table size
SELECT relname,pg_size_pretty(pg_total_relation_size(oid))
FROM pg_class WHERE relname IN ('f','n');
-- checking sped
EXPLAIN (analyze, buffers, verbose) SELECT min(id), max(id), sum(val) FROM f;
EXPLAIN (analyze, buffers, verbose) SELECT min(id), max(id), sum(val) FROM n;
现在结果大不相同:
422 MB
vs 498 MB
float8
的平均时间为2272.833ms
numeric(21,8)
3289.542ms
现在,这肯定不能反映真实情况,但在我看来:
numeric
会为关系的大小添加一些东西(对我来说是20%); 说实话,我对这些数字感到非常惊讶。两个表都是完全缓存的,因此只花时间来处理元组并进行数学计算。我认为这是一个更大的区别。
就个人而言,我现在会选择numeric
类型,因为它没有提供如此大的性能差异和数据精度。
答案 1 :(得分:1)
我加5美分。 PG 9.4
CREATE TABLE orders(
count integer not null
...
cost character varying(15) -- cost as string '10.22' for example
ncost numeric(10,2) -- same cost as numeric 10.22
)
~260000行:
解释分析
select sum(count*ncost) from orders
“总运行时间:743.259 ms”(10次测试后的热数据) 解释分析
select sum(count*cost::numeric(10,2)) from orders
“总运行时间:577.289毫秒” 因此,将sum()的成本保持为字符串更快。