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

pun*_*ish 21 postgresql

我想在Postgres中存储每个记录7个8位整数值.Pg不提供单字节整数类型,SMALLINT或2字节,是最小的整数数据类型.无论如何我可以存储我的7个8位数字并节省空间吗?

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

还有其他建议吗?

Gre*_*ith 17

鉴于PostgreSQL中任何行的开销是23字节(HeapTupleHeaderData),如果你真的关心少量空间,那么你可能选择了错误的存储数据的方法.

无论如何,因为所有更复杂的类型都有自己的开销(bytea增加了4个字节的开销,例如,位串5到8),完成你要找的东西的唯一方法是使用bigint(8字节),以数字方式移动每个值并将结果进行OR运算.您可以使用位串操作来使代码更容易 - 计算为位串,然后在存储之前转换为bigint - 或者如果您希望速度更好,则只需手动乘法/添加.例如,以下是如何将两个字节一起存储为两个字节的结构,然后再将它们返回:

int2 = 256 * byte1 + byte2
byte1 = int2 / 256
byte2 = int2 % 256
Run Code Online (Sandbox Code Playgroud)

您可以将相同的想法扩展到以这种方式存储其中的7个.检索开销仍然很糟糕,但实际上你会在这个过程中节省一些空间.但与行标题相比并不是很多.

  • 每行有6 x 1字节整数(?),3 x 2字节整数(SMALLINT)和2 x 4字节整数(INT).这是每行总共20个字节,加上Pg的开销.由于Pg仅提供SMALLINT作为最小值,因此我的6个1字节值将占用12个字节.我正在寻找可能的替代方案.节省6个字节乘以120亿行约为670 GB(如果我的计算是正确的).也就是说,我确实需要单独取出这些值,所以我可能会以检索速度付出代价.我需要平衡两者.在更大的计划中,0.7 TB并不是一个巨大的空间. (3认同)

小智 6

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)
Run Code Online (Sandbox Code Playgroud)

  • @ClodoaldoNeto 不正确,它可以从 -128 计数到 127,尝试 `select i, ascii(i::"char") from generate_series(-128, 127) s(i)` (5认同)

Eva*_*oll 5

"char"

这是 PostgreSQL 中的一个字节类型,适用于 -128,127 的范围。从文档中,

类型"char"(注意引号)的不同之处char(1)在于它只使用一个字节的存储空间。它在系统目录中作为一种简单的枚举类型在内部使用。

您可以将其偏向 [-128,127],方法是在写入数据库之前从 [0-255] 范围内的任何输入中减去 128,并在您从数据库中读取时将其添加回输出。

-- 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);
Run Code Online (Sandbox Code Playgroud)

输出的小摘录

 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
...
Run Code Online (Sandbox Code Playgroud)

我们使用generate_series(1,255)只因为chr(0)throws 因为你不能生成或输出 ASCII NUL(PostgreSQL 使用 cstrings)

pguint 延期

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

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