Sql 将数据存储为数字和双精度
我有一个带有数字字段的表格,比如:Sql 将数据存储为数字和双精度,sql,performance,postgresql,numeric,Sql,Performance,Postgresql,Numeric,我有一个带有数字字段的表格,比如: create table data ( id bigserial PRIMARY KEY, quantity numeric(21,8) NOT NULL ) 我需要一个numeric类型,因为有些查询需要一个从double中无法获得的精度级别 但我也有一些查询,这些数量加起来有数百万个,不关心取整问题,需要尽可能快 是否有一个标准的策略来做到这一点,或者我应该复制每个数字: create table data ( id bigserial PR
create table data (
id bigserial PRIMARY KEY,
quantity numeric(21,8) NOT NULL
)
我需要一个numeric
类型,因为有些查询需要一个从double中无法获得的精度级别
但我也有一些查询,这些数量加起来有数百万个,不关心取整问题,需要尽可能快
是否有一个标准的策略来做到这一点,或者我应该复制每个数字:
create table data (
id bigserial PRIMARY KEY,
quantity_exact numeric(21,8) NOT NULL,
quantity double precision NOT NULL
)
请阅读下面的更新以获得完整的视图
让我们比较一下您概述的两种情况:
float8
或numeric(21,8)
?)或保留两个
)-1
成为保留两个
的情况SELECT 'float8'::coltyp, pg_column_size(random()::float8) UNION ALL
SELECT 'numeric(21,8)', pg_column_size(random()::numeric(21,8));
在这种情况下,保留这两个列将需要两倍的空间。因此-1
到保留两个大小写,并且+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次运行):
- 对于
和float8
- 对于
,不超过数字(21,8)
17325.514ms
+1
用于float8
情况。这是一个仅限内存的测试,查询表(和冷表)将需要更多的时间float8
似乎是一个显而易见的方法(+1.5
与-2
)。您可以在此表顶部创建一个视图,该视图将同时发布原始float8
和铸造numeric(21,8)
,以满足您的查询的高精度要求
更新:在
一匹没有名字的马发表评论后,我决定重新测试,这次使用真实的表。我选择了9.4beta3,因为9.4附带了非常好的模块
这就是我所做的:
export PGDATA=$HOME/9.4b3
initdb -k -E UTF8 -A peer
pg_ctl start
然后,我使用新功能更改了一些默认设置:
通过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;
现在的结果大不相同:
- 大小为
422MB
vs498MB
float8
的平均时间为2272.833ms
- 对于
数字(21,8)
它是3289.542ms
当然,这并不反映真实情况,但在我看来:
- 使用
numeric
会给关系的大小增加一些东西(对我来说是20%)李>
- 在某种程度上降低查询速度(对我来说是44%)
老实说,我对这个数字感到非常惊讶。这两个表都被完全缓存,所以只花时间处理元组和计算。我想这会有更大的区别
就我个人而言,考虑到性能差异和数据精度都不太大,我现在会选择数字类型。第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
select sum(count*cost::numeric(10,2)) from orders
“总运行时间:743.259毫秒”(10次测试后的热数据)
解释分析
select sum(count*ncost) from orders
select sum(count*cost::numeric(10,2)) from orders
“总运行时间:577.289毫秒”
因此,对于sum(),将成本作为字符串保持得更快。您衡量过数字的性能了吗?和双打相比真的那么糟糕吗?通常,在SQL中处理“数百万个值”时,访问数据的时间会主导数据的处理。IMO“需要非常快”不是一个可操作的要求。首先让它使用numeric
。如果你能测量性能增益,我会非常惊讶。如果有,我也不会感到惊讶,因为你正在增加表的大小,从而增加聚合过程中需要读取的数据量。@GordonLinoff使用numeric可以很好地工作,但是添加数百万个numeric
要比添加数百万个double
s慢。我相信是这样。对于其中一些查询,性能是至关重要的。差异是可以测量的:在我的计算机上,求1000万个数值的和大约需要2.2秒。对1000万个浮点值求和只需1.5秒。考虑到float
的准确性问题,我仍然会坚持使用numeric
列,直到我遇到性能障碍。在视图中将float
转换为numeric
不会神奇地使float
值精确。如果值的来源不正确,简单地将其转换为不同的表示形式将无法解决基础存储的问题。此外,此代码可能正在测试将值转换为数值
与浮点
(第二种情况下不进行转换),而不是数字相加的速度。@a_horse_和_no_name,是的,你是对的。我现在已经在一个真正的桌子上测试过了。