hstore:在一格里装键值映射
hstore 扩展把文本键与文本值组成的扁平映射存进一个 PostgreSQL 值,其中值还可以为空。它适合不同记录拥有不同可选描述属性的情况,但灵活性不会免除重要键定义、值校验和按查询设计索引的工作。权限开关、价格和凭据都需要认真建模,不能因为装在一列里,就被当成没有后果的普通备注。 分清缺失、空值与文本 键不存在,与键存在但值为空,是两种状态。-> 对这两种情况都会返回 SQL NULL;? 用来确认键是否出现,defined 则检查它是否对应非空值。带引号的字符串“NULL”只是普通文本。整列本身还可以是 SQL NULL;如果不需要这个额外状态,应把映射列声明为 NOT NULL,再明确单个属性如何表示未知或缺失。 键不能是空值,键值对的顺序也没有语义。重复写同一个键不会生成列表,而只会留下其中一个值,且没有保证留下哪一个。需要确定性处理时,应在存储前拒绝重复键。应用应使用驱动支持的适配方式或参数化构造函数,不能把不可信字符串直接拼接成 hstore 字面量。驱动是否自动读写字典取决于实际支持和配置,不是任何语言环境都成立的承诺。 映射仍然可以有约束 CHECK 可以检查映射并限制指定属性,所以“数据库无法限制 hstore 内容”的说法不正确。但 SQL CHECK 在表达式结果为未知时也允许通过,单纯比较一个可能为空的提取结果,可能意外放行缺失键或空值。应明确出现条件、空值政策和允许值,再分别测试这些边界。需要时,提取键上的唯一表达式索引也可以约束重复值,并非只有普通列才能声明这种规则。 经常参与关联或业务约束的重要字段,仍然通常更适合独立的有类型列。理由是结构更清楚、操作更简单,而不是映射完全不能校验。增加一个可选键也许不需要改表,但改变键的含义仍可能需要历史数据检查、回填、客户端协调和索引调整。灵活性把这些决策移到另一处,并没有让迁移成本自然消失,更不能保证所有已有记录已经满足新含义。 对齐操作符与转换规则 GIN 和 GiST 支持 hstore 的包含与键存在操作符。提取文本再比较则属于另一种表达式,可能需要单独的 B-tree 表达式索引。给整个映射建 GIN,不意味着所有键值计算都自动有索引。应比较应用最终发出的 SQL、条件选择性和实际计划,再决定是否值得承担存储与写入开销;小表选择顺序扫描,也不能单独证明索引无效。 标准 hstore 到 jsonb 的转换把非空值保留为 JSON 字符串,把空值变成 JSON null,不会猜测文本“12”应该成为数字。反方向没有内置、对称的 jsonb 到 hstore 强制转换;任意嵌套 JSON 需要先定义允许的形状以及具体转换政策。不能把两个方向都描述成一条语句就能无损来回的免费迁移。存储大小也应由真实数据测量,不能承诺 hstore 总是更小。 ...