PostgreSQL --- 二进制数使用详解

PostgreSQL --- 二进制数使用详解 一“二进制”概述在 PostgreSQL 中处理“二进制”数据通常涉及两个完全不同的概念一个是位串Bit Strings用于底层数据的按位操作一个是bytea数据类型用于存储真正的原始字节流如图片、文件等。以下是针对这两种类型的全面解析(1)、 位串类型Bit Strings位串类型专门用于存储和操作由0和1组成的二进制数值序列。它分为固定长度的bit(n)和可变长度的varbit(n)。1. 核心特性与转换规则数值逻辑 vs 字符串逻辑将整数转为bit(8)遵循的是数值逻辑高位补零而将一个较短的位串显式转换为较长的位串时遵循的是字符串逻辑右侧/低位补零。转回整数使用::integer或CAST()可以安全地将位串还原为十进制整数。2. 丰富的位运算支持PostgreSQL 提供了完备的按位运算符包括按位与 ()、按位或 (|)、按位异或 (#)、按位取反 (~)以及左移 () 和右移 ()。在进行这些运算时必须确保参与运算的两个位串长度一致。(2)、 bytea 数据类型原始字节流当需要存储程序员认为是“原始字节raw bytes”的数据例如图像、音频、PDF文件或加密数据时应使用bytea类型。1. 与普通文本字符串的区别允许特殊字符普通字符串不允许包含值为零的字节或其他不可打印的字节而bytea完全支持。不受区域设置影响对文本字符串的处理取决于数据库的字符集编码和区域设置而对bytea的操作纯粹是处理实际的物理字节。2. 输入与输出格式bytea支持两种外部表示格式十六进制格式Hex推荐这是自 PostgreSQL 9.0 引入的现代格式。它将每个字节编码为两个十六进制数字并在整个字符串前加上\x作为标识符例如\xDEADBEEF。这种格式兼容性强且转换速度快是新应用的首选。转义格式Escape传统将不可打印的字节转换为以反斜杠开头的三位八进制值如\000。由于这种格式容易模糊二进制数据和文本字符串的界限官方强烈建议在新应用中避免使用。3. 常用函数与操作符PostgreSQL 提供了一系列内置工具来操作bytea数据连接使用||运算符可以将多个二进制字符串拼接在一起2。长度计算使用octet_length(string)返回二进制字符串的实际字节数2。子串提取与替换支持substring()、position()查找位置以及overlay()进行字节级别的替换2。编解码转换可以使用encode(data, hex/base64)将二进制数据转为可读的文本字符串或使用decode(text, format)将 Base64 等格式的文本还原为二进制数据6。 最佳实践建议明确需求如果是为了掩码、状态标志等数学运算请使用bit/varbit如果是为了存储文件附件或非结构化数据请使用bytea。统一格式在使用bytea时保持默认的bytea_output hex配置以确保最佳的跨平台兼容性和性能。注意类型匹配在对bytea数据进行操作时务必确保参与运算的值都是bytea类型避免直接将普通的text字符串与其混合运算而导致报错。二位串类型Bit Strings使用详解(1)整数转换为固定长度的位串PostgreSQL 提供了强大的位串Bit String类型可以通过将整数转换为固定长度的位串来实现这一需求。以下是几种常用的转换方法1. 使用bit(n)进行固定长度转换这是最标准且推荐的方法。可以将十进制整数显式转换为bit(n)类型其中n是输出的二进制位数。SELECT 10::bit(8); -- 返回结果: 00001010 (保留前导零)2. 去除前导零如果不需要补齐的前导零希望看到最原始的二进制数值可以结合LTRIM函数来去掉左侧多余的0SELECT LTRIM(10::bit(32)::text, 0); -- 返回结果: 10103. 自定义to_bin()函数在 PostgreSQL 中并没有像 MySQL 那样直接名为to_bin()或BIN()的内置函数。如果希望在项目中频繁调用该功能可以在数据库中创建一个自定义函数之后就可以直接使用to_bin(数值)的方式调用了CREATE OR REPLACE FUNCTION to_bin(val integer) RETURNS text AS $$ BEGIN -- 保留64位长度并使用 LTRIM 去掉多余的前导0 RETURN LTRIM(val::bit(64)::text, 0); END; $$ LANGUAGE plpgsql; -- 测试使用 SELECT to_bin(42); -- 返回结果: 101010⚠️ 注意事项在进行转换时请确保指定的bit(n)长度n足够容纳您的十进制数否则高位数据会被截断。PostgreSQL 的标准库默认不支持直接将smallint(int2) 强制转换为bit类型。如果您的数字是int2类型建议先将其转换为integer(int4) 或bigint再进行bit转换2。(2)表示二进制数在 PostgreSQL 中“表示二进制数”通常分为两种情况一是表示二进制的数值位串二是存储和处理真正的原始二进制数据字节流。以下是具体的表示方法1. 使用B...语法表示二进制数值如果需要在 SQL 语句中直接书写一个由0和1组成的二进制数值可以使用B...或b...的格式。这种写法常用于位运算或赋值给位串类型bit/varbit。SELECT B10101010; -- 返回结果: 101010102. 使用\x前缀表示十六进制二进制数据如果要向bytea可变长度二进制字符串字段插入真实的二进制数据如图片、加密文件等PostgreSQL 默认且推荐使用十六进制格式。该格式会将每个字节用两个十六进制数字表示并在整个字符串前面加上\x作为前缀标识。SELECT \xDEADBEEF::bytea; -- 返回结果: \xdeadbeef注除了十六进制格式外PostgreSQL 还支持历史遗留的“转义格式”将不可打印字符转换为\xxx形式的八进制值但由于其容易与文本字符串混淆且处理笨拙官方强烈建议在新应用中避免使用。3. 使用字面量前缀进行十进制与二进制的转换标示在较新的 PostgreSQL 版本中支持直接使用带前缀的字面量来表示不同进制的整数并可以将其转换为常规数据类型二进制标示 (0b)例如0b10101010::int4结果为十进制的 170十六进制标示 (0x)例如0xdecaf::int4结果为十进制的 912559八进制标示 (0o)例如0o666::int4结果为十进制的 4384. 内置函数转换输出为二进制字符串如果将普通的十进制整数直接转换为以0和1组成的文本字符串可以使用以下函数截止PostgreSQL 16还没有相关函数可以自定义。to_bin()在最新的 PostgreSQL 版本中引入专门用于将整数转为二进制字符串。例如select to_bin(1234567);会返回100101101011010000111。to_hex()将整数转为十六进制字符串。例如select to_hex(1234567);会返回12d687。(3)二进制数运算在 PostgreSQL 中对两个二进制数位串进行位运算非常直接。PostgreSQL 提供了一系列专门的按位运算符来支持这些操作。假设有两个二进制数A B10101010和B B11001100以下是具体的位运算方法和示例1. 按位与 ()如果两个对应位都为 1结果位才为 1否则为 01。SELECT B10101010 B11001100; -- 返回结果: 100010002. 按位或 (|)只要两个对应位中有一个为 1结果位就为 11。SELECT B10101010 | B11001100; -- 返回结果: 111011103. 按位异或 (#)当两个对应位不同时结果位为 1相同时为 01。SELECT B10101010 # B11001100; -- 返回结果: 011001104. 按位取反 (~)这是一个一元运算符将所有的 1 变为 0所有的 0 变为 11。SELECT ~B10101010; -- 返回结果: 010101015. 位移运算 (左移 /右移)左移 ()将所有位向左移动指定的位数左边丢弃右边补 01。右移 ()将所有位向右移动指定的位数右边丢弃正数左边补 0负数左边补 11。-- 左移 2 位 SELECT B10101010 2; -- 返回结果: 10101000 -- 右移 2 位 SELECT B10101010 2; -- 返回结果: 00101010⚠️ 注意事项在进行位运算时参与运算的两个二进制位串的长度必须相同。如果长度不一致PostgreSQL 会抛出错误。可以使用lpad函数手动补齐前导零或者将其转换为相同长度的bit(n)类型后再进行运算。(4)“位串”与“整数”转换在 PostgreSQL 中将bit(8)转换回整数非常简单。使用类型转换操作符::或标准的CAST()函数来实现1. 使用::操作符推荐最简洁SELECT 00000011::bit(8)::integer; -- 返回结果: 32. 使用CAST()函数SELECT CAST(00000011::bit(8) AS integer); -- 返回结果: 3⚠️ 注意事项必须指定目标整数类型如果只写00000011::bit(8)::bit由于不指定长度时默认等同于bit(1)它只会截取整数的最低位导致结果变为1。因此请务必显式转换为integer或int4、bigint等。负数处理PostgreSQL 同样支持包含符号位的二进制字符串转回整数。例如cast(111111010100 as bit(12))::integer会正确还原为-44。(5)“整数转位串”和“位串长度转换”现象分析分析 select 3::bit(8), (B11)::bit(8) 结果 00000011 11000000为啥不一致这两个结果之所以不一致是因为 PostgreSQL 在处理“整数转位串”和“位串长度转换”时遵循了不同的底层规则1.3::bit(8)的结果是00000011数值逻辑当一个十进制整数直接转换为bit(n)类型时PostgreSQL 会将其视为数值的二进制表示。数字 3 的二进制形式是11。在扩展为 8 位长度时系统会在左侧补齐前导零非负数补0以保留其原本的数值大小。因此结果为00000011。2.(B11)::bit(8)的结果是11000000字符串截断/填充逻辑当一个已经存在的位串常量进行显式长度转换时PostgreSQL 遵循的是 SQL 标准的字符串处理规则B11本身是一个只有两位的位串。当显式将其转换为更长的bit(8)类型时系统会在其右侧补齐零即低位补零。因此原本在最左边的11被保留在高位后面补上六个0最终结果变成11000000。 总结与最佳实践整型转位串按数值逻辑转换高位补零如3::bit(8)-00000011。位串转位串按字符串逻辑转换右侧补零如B11::bit(8)-11000000。如果希望将B11也当作数值来处理并得到00000011可以先将其转为整数再转为指定位数的位串。SELECT (B11::bit(2)::integer)::bit(8); -- 可以不写B SELECT (11::bit(2)::integer)::bit(8); -- 返回结果: 00000011三 bytea 数据类型原始字节流使用详解在 PostgreSQL 中byteaByte Array数据类型专门用于存储“原始字节流”raw bytes例如图像、音频、视频或加密数据。与普通文本字符串不同它允许存储零值字节和其他不可打印的字符且处理过程不受数据库区域设置和字符集编码的影响。以下是操作bytea数据类型的核心指南1. 数据的输入与输出格式bytea支持两种外部表示格式PostgreSQL 默认使用十六进制格式Hex十六进制格式推荐将二进制数据编码为每字节两个十六进制数字并在整个字符串前加上\x作为标识符。例如\xDEADBEEF。这种格式兼容性强且转换速度快。转义格式传统将不可打印的字节转换为以反斜杠开头的三位八进制值如\000。由于容易模糊二进制数据和文本字符串的界限官方建议在新应用中避免使用。2. 基础 SQL 操作创建表后可以直接使用标准的 SQL 语句对bytea字段进行增删改查-- 1. 创建包含 bytea 字段的表 CREATE TABLE images ( id serial PRIMARY KEY, name varchar(50), data bytea ); -- 2. 插入二进制数据使用 \x 语法 INSERT INTO images (name, data) VALUES (image1, E\\x89504e470d0a1a0a); -- 3. 读取二进制数据 SELECT name, data FROM images; -- 4. 更新二进制数据 UPDATE images SET data E\\x776f726c6421 WHERE name image1;3. 查看明文与编解码转换由于直接查询bytea字段通常会返回带有\x前缀的十六进制字符串如果您需要查看其可读形式可以使用以下内置函数encode()函数将二进制数据转换为指定的文本格式如hex、base64或escape。SELECT encode(data, hex) AS hex_text, encode(data, base64) AS base64_text FROM images; -- 输出 -- 776f726c6421 d29ybGQhconvert_from()函数如果bytea内部原本存储的是特定字符集的文本如 UTF-8可以将其正确解码为可读字符串。SELECT convert_from(data, UTF8) AS plain_text FROM images; -- 输出 -- world!注意如果原始数据是图片等非文本内容强行转为文本会显示乱码。4. 常用内置函数与操作符PostgreSQL 提供了丰富的工具来处理二进制字符串连接使用||运算符拼接多个二进制字符串。长度计算使用octet_length(bytea_column)获取实际占用的字节数。子串提取与查找支持substring()提取部分字节以及position()查找特定字节序列的位置。还原为二进制使用decode(text, format)可以将 Base64 等格式的文本重新转换回bytea数据。5. 容量限制与性能注意事项最大长度bytea理论上可以存储高达1 GB(230230 字节) 的数据5。TOAST 机制当单个bytea字段的数据超过一定阈值默认约 2KB时PostgreSQL 会自动启用 TOAST超大属性存储技术将其压缩并分割存储在独立的 TOAST 表中从而不影响主表的查询性能5。最佳实践尽管bytea可以存储大文件但对于大于 10MB 甚至 100KB 的二进制文件通常建议将其存储在文件系统或对象存储中仅在数据库中保存文件路径以避免高昂的网络传输开销和内存消耗5。