在 postgres 中,在多个表和一对多关系上使用 json 字段有什么真正的缺点吗?

pau*_*l23 5 postgresql database-design

在我们当前的应用程序中,我们基本上将为每个字段提供称为“翻译”的东西。所以一个表最初是这样的:

CREATE TABLE organisation (
   id SERIAL PRIMARY KEY,
   name TEXT NOT NULL DEFAULT '',
)
Run Code Online (Sandbox Code Playgroud)

但是,该名称现在将取决于语言。

基本的解决方案是不再将名称字段存储在组织表中,而是存储在“organisation_language”表中:

CREATE TABLE organisation (
   id SERIAL PRIMARY,
);  


CREATE TABLE organisation_language
(
  id SERIAL PRIMARY KEY,
  organisation INTEGER NOT NULL,
  field_name TEXT NOT NULL,
  field_language TEXT NOT NULL,
  field_translation TEXT NOT NULL DEFAULT '',
  
  FOREIGN KEY (organisation)
    REFERENCES public.organisation (id) MATCH SIMPLE
      ON UPDATE CASCADE
      ON DELETE CASCADE
      NOT VALID
);
Run Code Online (Sandbox Code Playgroud)

(是的,我故意忽略了我可以为语言键添加查找表en-US并参考它)。

但是,现在我不能再“保证”name定义每个字段(如)。当然,代码可以确保它确实如此,但那是另一层。它还增加了相当多的复杂性,因为不再容易看到哪些字段甚至属于数据模型。或者为某个组织提供什么翻译。

如果我查看 JSON,我实际上会“喜欢”我的后端在请求“来自组织 1 的数据”时发送到前端,它看起来像:

{
    id: 1
    name: {
        "en-us": 'hello world',
        "nl-nl": 'hoi wereld'
    }
}
Run Code Online (Sandbox Code Playgroud)

最重要的是,发生的主要操作是“插入”——然后一次插入所有数据(所以一次所有翻译),并检索所有语言数据,而不是单一语言。(由于“路径”,例如en-gbcheck中不存在字段en-us,并且此计算发生在后端)。

修改很少发生,我们甚至考虑“不支持它”,而是采用写时复制和停用旧的“组织”。


这让我想到,将“名称”字段的类型更改为 JSON 而不是语言表是一个坏主意吗?(并对所有字段执行此操作)。然后它看起来像:

CREATE TABLE organisation 
(
  id SERIAL PRIMARY KEY,
  name JSON NOT NULL DEFAULT '{}'::JSON
);
Run Code Online (Sandbox Code Playgroud)

插入到 name 字段的数据将是(后端必须验证架构):

{
    "en-us": 'hello world',
    "nl-nl": 'hoi wereld'
}
Run Code Online (Sandbox Code Playgroud)

与使用语言表相比,使用 JSON 字段是否存在实际缺陷?它具有更容易查找和插入(或删除)的优点。而且由于无论如何都不应该进行修改,因此没有缺点 - 对吗?

或者 PostgreSQL 会在以后窒息吗?

Lau*_*lbe 5

第一个范式要求列是单个原子值。我认为,如果您真的将这些 JSON 对象视为存储和检索的对象,那么从数据库的角度来看它们是原子的,并且您计划执行的操作没有问题。

如果您想在条件中使用 JSON 的一部分WHERE,那仍然没问题。如果您想要基于 JSON 部分的数据库约束,或者想要基于 JSON 属性进行连接,我建议不要使用这样的数据模型。