22

我目前正在尝试修改与 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 都可以就如何解决这个问题提出建议吗?我最近才回到数据库交互的深层次,所以我承认我有点生疏了。

4

4 回答 4

33

You've basically got two choices:

Option 1: A sparse table

Have one column for each data type, but only use the column that matches that data type you want to store. Of course this leads to most columns being null - a waste of space, but the purists like it because of the strong typing. It's a bit clunky having to check each column for null to figure out which datatype applies. Also, too bad if you actually want to store a null - then you must chose a specific value that "means null" - more clunkiness.

Option 2: Two columns - one for content, one for type

Everything can be expressed as text, so have a text column for the value, and another column (int or text) for the type, so your app code can restore the correct value in the correct type object. Good things are you don't have lots of nulls, but importantly you can easily extend the types to something beyond SQL data types to application classes by storing their value as json and their type as the class name.

I have used option 2 several times in my career and it was always very successful.

于 2013-08-14T13:59:10.993 回答
5

另一种选择,取决于你在做什么,可能只有一个值列,但在值周围存储一些 json ......

这可能看起来像:

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

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

于 2019-07-08T03:47:49.737 回答
3

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

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

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

于 2013-08-14T14:41:16.467 回答
2

您可以使用 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-05-27T08:38:16.830 回答