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
      vs
      498MB
    • 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,是的,你是对的。我现在已经在一个真正的桌子上测试过了。