【问题标题】:How to maintain multiple branches for a primary key (col1) and a json datatype column如何为一个主键 (col1) 和一个 json 数据类型列维护多个分支
【发布时间】:2021-08-16 03:01:16
【问题描述】:

我有一个设计问题,我知道 JSON 是最佳选择,但仍面临实施问题。

要求:

我需要存储业务实体信息。属性有(registration_NO(主键)、地址、电话号码(多个电话号码)、董事(多人)等。 由于registration_number和(电话号码,董事)之间是一对多的关系,我决定使用jsonb数据类型。

有两个条件。

  1. 电话号码、董事可以添加、删除等。
  2. 对记录的任何新更改都需要批准。在获得批准之前,将显示旧值。

我面临的问题: 比如说: 当前记录:Regno:directors,record_status(故意没有显示电话号码)

              100: {John, Joe}, current; ok to display

当我添加新董事时,此条目必须是注册的新分支#主键状态为“尚未批准”,因此不应出现在查询中。

              100: {john, joe, jane}, updated; not yet approved.

但是,当我更新该行时,directors 的旧值被覆盖。 当我插入状态为“更新;尚未批准”时,它会插入重复的注册记录#。

理想情况下,我希望有 2 个分支进行注册# 100 ;一个获准展示,一个未获批准展示。

我应该如何处理这个问题?任何帮助表示赞赏。

【问题讨论】:

  • 旁注:架构对我来说看起来很静态。所以 JSON 不是“最好的选择”。您应该考虑不要滥用 JSON,而是使用关系方式,例如(查找和/或链接)表和列。
  • Blue Star 和 Sticky bit 绝对正确:不要存储 JSON。进程之间的通信很好,但存储很糟糕。将您的数据标准化存储,然后如果需要,您可以导出为 JSON。请参阅 JSON Functions and Operators 上的 Postgres 文档。

标签: json postgresql


【解决方案1】:

经验法则:如果您的数据具有可预测的结构,您可能不想使用 JSON 列。

我会用三个表来实现它。一张表用于业务实体,一张用于电话号码,一张用于董事。像这样的:

CREATE TABLE business_entity (
    registration_no uuid, 
    address text, 
    PRIMARY KEY(registration_no)
);

CREATE TYPE status AS ENUM ('added', 'deleted');

CREATE TABLE business_entity_phone_number (
    registration_no uuid, 
    phone_number text, 
    status status, 
    CONSTRAINT fk_business_entity_phone_number 
        FOREIGN KEY(registration_no) REFERENCES business_entity(registration_no)
);

CREATE TABLE business_entity_director (
    registration_no uuid, 
    director text, 
    status status, 
    CONSTRAINT fk_business_entity_director 
        FOREIGN KEY(registration_no) REFERENCES business_entity(registration_no)
);

状态列用于跟踪更改。因此,如果添加了新的电话号码,我们会将该行标记为“已添加”,直到它获得批准;如果删除了某行,我们将其保留在原处,但将其标记为“已删除”,直到它获得批准。

然后,为了查询我们的业务实体,我们只需要省略“添加”行:

SELECT 
    be.registration_no, 
    address, 
    (
        SELECT ARRAY_AGG(phone_number) 
        FROM business_entity_phone_number bepn
        WHERE be.registration_no = bepn.registration_no 
        AND COALESCE(bepn.status, 'deleted') != 'added'
    ) as phone_numbers, 
    (
        SELECT ARRAY_AGG(director) 
        FROM business_entity_director bed
        WHERE be.registration_no = bed.registration_no 
        AND COALESCE(bed.status, 'deleted') != 'added'
    ) as directors
FROM business_entity be

这个解决方案没有完全处理的事情:

  • 跟踪business_entity 中其他字段的更改(从问题中不清楚是否需要这样做)
  • 电话号码/董事的更新(或者添加/删除是否足够?)
  • 当两个用户尝试进行相互冲突的更改时会发生什么?

【讨论】:

  • 谢谢@蓝星。我有一个评论,可能是我一直在处理的问题的答案。我不喜欢多表选项。这就是我们最初的设计。这增加了设计和维护的复杂性,明天当添加新属性时,我必须在 DB 级别开始设计,进行 API 更改等等。另一个原因,即使我知道结构,内容也可能会有所不同。例如:电话号码和董事会+董事...我认为将它们放在单独的表格中并不理想。我想出了这个问题的答案。
  • 我决定将它作为复合主键的一部分,而不是将 record_status 作为 json 属性,因为我只有两个选择,一个可以显示的当前行和一个更新的行正在通过审批,无法显示。一旦获得批准,将触发一个触发器,它将“not_ok_to_display”json 数据复制到“current”记录,并且更改将可见。 not_ok_to_display 行的“批准”将再次为 NULL,等待新的更新和批准。我想知道是否有任何 cmets 可以改进这个设计。
猜你喜欢
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-04
  • 2021-07-04
相关资源
最近更新 更多