代码之家  ›  专栏  ›  技术社区  ›  punkish

是否可以在Postgres中存储一个1字节的数字?

  •  21
  • punkish  · 技术社区  · 14 年前

    我想在Postgres中存储每个记录7个8位整数值。pg不提供单字节整型、smallint或2字节,这是最小的整型数据类型。我是否可以存储7个8位数字并节省空间?

    具有7元素数组的数组类型是否更紧凑?或者,我应该对我的7个数字进行二进制表示(例如,在Perl中使用pack)并将其存储在单个字节字段中吗?

    还有什么建议吗?

    7 回复  |  直到 7 年前
        1
  •  17
  •   Greg Smith    14 年前

    考虑到PostgreSQL中任何一行的开销是 23 bytes (heaptupleheaderdata),如果你真的这么在乎少量的空间,那么你可能选择了错误的方法来存储数据。

    不管怎样,因为所有更复杂的类型都有自己的开销(字节增加了四个字节的开销,例如位字符串5到8),所以要完成您所寻找的工作,唯一的方法是使用一个bigint(8个字节),用数字移动每个值,或者将结果组合在一起。您可以使用 bit string operations 为了使代码更容易——计算为位字符串,然后在存储之前强制转换为bigint——或者,如果您希望速度更好,只需手动进行乘法/加法。例如,下面介绍如何将两个字节一起存储到一个双字节结构中,然后再将它们返回:

    int2 = 256 * byte1 + byte2
    byte1 = int2 / 256
    byte2 = int2 % 256
    

    您可以通过这种方式将相同的想法扩展到存储其中的7个。检索开销仍然是可怕的,但实际上在这个过程中您已经节省了一些空间。但与行标题无关。

        2
  •  4
  •   LPL user462990    13 年前

    pg_catalog.char (另一个符号-“char”)类型,只使用1个字节来存储其值。

    select pg_column_size( 'A' );
    pg_column_size
    ----------------
                  2
    (1 row)
    
    select pg_column_size( 'A'::"char" );
    pg_column_size
    ----------------
                  1
    (1 row)
    
        3
  •  2
  •   user80168    14 年前

    是否使用这些值查找记录?

    如果是-使用像int4这样的普通数据类型(如果使用64位体系结构,甚至是int8)。

    如果不是,首先问问你自己,将这些值存储在pg中有什么意义?您可以使用字节(复杂的I/O)或位串(甚至更复杂的I/O),但要点是什么?你将有多少十亿个记录?您是否检查过较小的数据类型使用较少的空间(提示:它没有,请检查它-是否涉及数据对齐问题)?您是否认为较小的数据类型更快(事实上,比较两个int2值比32位体系结构上的两个int4值更复杂)。

        4
  •  1
  •   Jordan S. Jones    14 年前
        5
  •  1
  •   Anonymous    14 年前

    首先你问了7个字节,但现在是6个字节。六个8位值正好对应于mac地址大小和postgresql的内置类型macaddr。您可以使用mac语法f.i.a1-b2-c3-d4-e5-f6插入这些字节。

        6
  •  1
  •   Hossein    12 年前

    我自己还没有测试过,但是有一些扩展;例如 http://pgxn.org/dist/tinyint/ .

        7
  •  1
  •   Evan Carroll    7 年前

    "char"

    这是PostgreSQL中的一个单字节类型,适合-128127的范围。从 the docs,

    类型 “沙尔” (注意引号)与 char(1) 在那里面 只使用一个字节的存储空间。 它在系统目录中内部用作简单的枚举类型。

    在写入数据库之前,您可以从[0-255]范围内的任何输入中减去128,然后在从数据库中读取数据时将其添加回输出,从而使其偏向于[-128127]。

    -- works
    SELECT (-128)::"char", 127::"char";
    
    -- generates out of range
    SELECT (-128)::"char";
    SELECT 128::"char";
    
    -- Shifts to unsigned range.
    -- If you're going to be using "char"
    -- review the results of this query!
    SELECT
      x::int AS "inputUnsigned",
      chr(x) AS "extendedASCII",
      -- this is the "char" types representation for that input.
      signed::"char" AS "charRepresentation",
    
      signed     AS "inputUnsignedToSigned",
      signed+128 AS "inputUnsignedToSignedToUnsigned"
    FROM generate_series(1,255) AS gs(x)
    -- Here we map the input in the range of [0,255] to [-128,127]
    CROSS JOIN LATERAL ( VALUES (x::int-128) )
      AS v(signed);
    

    输出的小摘录

     inputUnsigned | extendedASCII | charRepresentation | inputUnsignedToSigned | inputUnsignedToSignedToUnsigned 
    ---------------+---------------+--------------------+-----------------------+---------------------------------
    ....
               190 | ¾             | >                  |                    62 |                             190
               191 | ¿             | ?                  |                    63 |                             191
               192 | À             | @                  |                    64 |                             192
               193 | Á             | A                  |                    65 |                             193
               194 | Â             | B                  |                    66 |                             194
               195 | Ã             | C                  |                    67 |                             195
               196 | Ä             | D                  |                    68 |                             196
    ...
    

    我们使用 generate_series(1,255) 只因为 chr(0) 因为无法生成或输出ASCII而引发 NUL (PostgreSQL使用CString)

    pguint 延伸

    Pguint 是提供两个一字节表示的扩展,

    • int1 (签名)
    • uint1 (未签名)