【问题标题】:Sane way to store different data types within same column in postgres?在postgres的同一列中存储不同数据类型的合理方法?
【发布时间】:2020-08-18 06:12:43
【问题描述】:

我目前正在尝试修改与 postgres 数据库交互的现有 API。长话短说,它本质上是存储描述符/元数据,以确定实际“资产”(通常是某种文件)在服务器硬盘上的存储位置。

目前,可以用任意数量的未定义键值对(即uploadBy、addOn、assetType等)对这些“资产”进行“标记”。这些标记存储在单独的表中结构类似于以下:

+---------------+----------------+-------------+
|assetid (text) | tagid(integer) | value(text) |
|---------------+----------------+-------------|
|someStringValue| 1234           | someValue   |
|---------------+----------------+-------------|
|aDiffStringKey | 1235           | a username  |
|---------------+----------------+-------------|
|aDiffStrKey    | 1236           | Nov 5, 1605 |
+---------------+----------------+-------------+

assetid 和 tagid 是来自其他表的外键。想想代表文件的assetid,而tagid/value对是描述符的映射。

现在,API(在 Java 中)将所有这些键值对创建为 Map 对象。这包括时间戳/日期等内容。我们想要做的是能够以某种方式为键值对中的值存储不同类型的数据。或者至少,在数据库中以不同的方式存储它,这样如果需要,我们可以运行查询检查这些标签上的日期范围等。但是,如果它们作为文本项存储在数据库中,那么我们必须 a.) 知道这实际上是一个日期/时间/时间戳项,并且 b.) 转换为我们可以实际运行的东西查询。

到目前为止,我只能想到一个想法,而无需完全改变数据库的布局。

就是扩展assettag表(如上所示),增加各种类型的列(数字、文本、时间戳),让它们为空,然后在插入时,检查对应的'key'以找出什么它确实是数据类型。但是,我可以看到这种实现存在很多问题。

任何 PostgreSQL-Ninjas 都可以就如何解决这个问题提出建议吗?我最近才回到数据库交互的深层次,所以我承认我有点生疏了。

【问题讨论】:

  • 这就是为什么使用 EAV 表存储数据通常是个坏主意的原因之一。
  • 我同意。我想继承现有设计的乐趣。 C'est la vie。

标签: postgresql database-design


【解决方案1】:

你基本上有两个选择:

选项 1:稀疏表

每种数据类型都有一列,但只使用与您要存储的数据类型匹配的列。当然,这会导致大多数列为空——浪费空间,但纯粹主义者喜欢它,因为它具有强类型。必须检查每一列是否为 null 以确定适用的数据类型有点笨拙。另外,如果你真的想存储一个空值,那就太糟糕了——那么你必须选择一个“意味着空”的特定值——更笨重。

选项 2:两列 - 一列用于内容,一列用于类型

一切都可以表示为文本,因此为值设置一个文本列,为类型设置另一列(int 或 text),以便您的应用代码可以在正确的类型对象中恢复正确的值。好事是您没有很多空值,但重要的是,您可以通过将类型的值存储为 json 并将它们的类型存储为类名,轻松地将类型扩展到 SQL 数据类型之外的应用程序类。

在我的职业生涯中,我曾多次使用选项 2,而且总是非常成功。

【讨论】:

  • 波西米亚人,感谢您的回复。如果我选择选项 2,我将如何对类型标记为日期/时间类型的值进行日期范围类型查询?最好编写一个存储过程的 postgresql 等价物来执行这种类型的查询?
  • 将日期存储为自 1979 年以来的天数,或者如果是时间戳类型,则存储自 1970 年以来的秒数。Google 纪元时间或 unix 时间。
  • 很好的答案,正是这个问题我遇到了并且无法决定。即使在我的情况下,'postgres' 非常有效地存储 'null' 值,你对个别类型的论点也说服了我。谢谢!
  • 选项 2range 查询的索引怎么样?我认为使用选项 2 你最终会得到一些你无法有效查询的东西......
【解决方案2】:

根据您的操作,另一种选择可能是只有一个值列,但在值周围存储一些 json...

这可能看起来像:

  {
    "type": "datetime",
    "value": "2019-05-31 13:51:36" 
  } 

这甚至可以更进一步,使用JsonXML 列。

【讨论】:

    【解决方案3】:

    我绝不是 PostgreSQL 忍者,但我认为您可以查看 hstore data type,而不是两列(一列用于名称,一列用于类型):

    用于将键/值对集合存储在单个中的数据类型 PostgreSQL 值。这在各种情况下都很有用,例如 具有许多很少检查或半结构化的属性的行 数据。键和值只是文本字符串。

    当然,您必须检查日期/时间戳如何转换为这种类型,看看它是否适合您。

    【讨论】:

      【解决方案4】:

      您可以使用 2 种不同的技术:

      1. 如果每个 tagid 都有浮动类型

      为每个 tagid-assetid 组合和实际数据表定义表和 ID:

      maintable:
      +---------------+----------------+-----------------+---------------+
      |assetid (text) | tagid(integer) | tablename(text) | table_id(int) |
      |---------------+----------------+-----------------+---------------|
      |someStringValue| 1234           | tablebool       | 123           |
      |---------------+----------------+-----------------+---------------|
      |aDiffStringKey | 1235           | tablefloat      | 123           |
      |---------------+----------------+-----------------+---------------|
      |aDiffStrKey    | 1236           | tablestring     | 123           |
      +---------------+----------------+-----------------+---------------+
      
      tablebool
      +-------------+-------------+
      | id(integer) | value(bool) |
      |-------------+-------------|
      | 123         | False       |
      +-------------+-------------+
      
      tablefloat
      +-------------+--------------+
      | id(integer) | value(float) |
      |-------------+--------------|
      | 123         | 12.345       |
      +-------------+--------------+
      
      tablestring
      +-------------+---------------+
      | id(integer) | value(string) |
      |-------------+---------------|
      | 123         | 'text'        |
      +-------------+---------------+
      
      1. 如果每个 tagid 都有固定类型

      创建tagid描述表

      tag descriptors
      +---------------+----------------+-----------------+
      |assetid (text) | tagid(integer) | tablename(text) |
      |---------------+----------------+-----------------|
      |someStringValue| 1234           | tablebool       |
      |---------------+----------------+-----------------|
      |aDiffStringKey | 1235           | tablefloat      |
      |---------------+----------------+-----------------|
      |aDiffStrKey    | 1236           | tablestring     |
      +---------------+----------------+-----------------+
      

      及对应的数据表

      tablebool
      +-------------+----------------+-------------+
      | id(integer) | tagid(integer) | value(bool) |
      |-------------+----------------+-------------|
      | 123         | 1234           | False       |
      +-------------+----------------+-------------+
      
      tablefloat
      +-------------+----------------+--------------+
      | id(integer) | tagid(integer) | value(float) |
      |-------------+----------------+--------------|
      | 123         | 1235           | 12.345       |
      +-------------+----------------+--------------+
      
      tablestring
      +-------------+----------------+---------------+
      | id(integer) | tagid(integer) | value(string) |
      |-------------+----------------+---------------|
      | 123         | 1236           | 'text'        |
      +-------------+----------------+---------------+
      

      所有这些都只是一般的想法。您应该根据自己的需要对其进行调整。

      【讨论】:

        猜你喜欢
        • 2016-03-17
        • 1970-01-01
        • 2017-03-10
        • 2017-05-06
        • 2020-09-05
        • 2012-07-03
        • 2012-02-29
        相关资源
        最近更新 更多