Postgresql 在postgres的同一列中存储不同数据类型的合理方法?
我目前正试图修改与postgres数据库交互的现有API。长话短说,它本质上是存储描述符/元数据,以确定实际“资产”(通常是某种文件)存储在服务器硬盘上的位置 目前,可以使用任意数量的未定义键值对(即uploadedBy、addedOn、assetType等)对这些“资产”进行“标记”。这些标记存储在一个单独的表中,其结构类似于以下内容:Postgresql 在postgres的同一列中存储不同数据类型的合理方法?,postgresql,database-design,Postgresql,Database Design,我目前正试图修改与postgres数据库交互的现有API。长话短说,它本质上是存储描述符/元数据,以确定实际“资产”(通常是某种文件)存储在服务器硬盘上的位置 目前,可以使用任意数量的未定义键值对(即uploadedBy、addedOn、assetType等)对这些“资产”进行“标记”。这些标记存储在一个单独的表中,其结构类似于以下内容: +---------------+----------------+-------------+ |assetid (text) | tagid(intege
+---------------+----------------+-------------+
|assetid (text) | tagid(integer) | value(text) |
|---------------+----------------+-------------|
|someStringValue| 1234 | someValue |
|---------------+----------------+-------------|
|aDiffStringKey | 1235 | a username |
|---------------+----------------+-------------|
|aDiffStrKey | 1236 | Nov 5, 1605 |
+---------------+----------------+-------------+
assetid和tagid是来自其他表的外键。想象一下表示文件的assetid和tagid/值对是描述符的映射
现在,API(Java)将所有这些键值对创建为映射对象。这包括时间戳/日期之类的内容。我们希望能够以某种方式为键值对中的值存储不同类型的数据。或者至少,在数据库中以不同的方式存储它,这样,如果需要,我们可以在这些标记上运行查询,检查日期范围等。但是,如果它们作为文本项存储在数据库中,那么我们必须a.)知道这实际上是一个日期/时间/时间戳项,b.)转换为我们可以实际运行此类查询的内容
到目前为止,我只想到了一个想法,没有完全改变数据库的布局太多
这是为了扩展assettag表(如上所示),为各种类型(数字、文本、时间戳)增加列,允许它们为null,然后在插入时检查相应的“键”,以确定它实际上是什么类型的数据。然而,我可以看到这种实现存在很多问题
有任何PostgreSQL忍者能就如何解决这个问题提供建议吗?我最近才回到数据库交互的最底层,所以我承认我有点生疏。你基本上有两个选择: 选项1:稀疏表 每个数据类型有一列,但只使用与要存储的数据类型匹配的列。当然,这会导致大多数列为null——这是一种浪费空间的行为,但是纯粹主义者喜欢它,因为它具有很强的类型性。必须检查每一列是否为null以确定应用哪种数据类型有点笨拙。另外,如果您真的想要存储一个空值,那么您必须选择一个“意味着空值”的特定值,这就太糟糕了——更笨重了 选项2:两列-一列用于内容,一列用于类型 所有内容都可以表示为文本,因此有一个文本列作为值,另一个列(int或text)作为类型,这样应用程序代码就可以在正确的类型对象中恢复正确的值。好的方面是您没有太多的空值,但重要的是,您可以轻松地将类型扩展到SQL数据类型以外的应用程序类,方法是将它们的值存储为json,将它们的类型存储为类名
在我的职业生涯中,我曾多次使用选项2,它总是非常成功。我不是PostgreSQL忍者,但我认为,与其使用两列(一列表示名称,一列表示类型),不如看: 用于在单个数据库中存储键/值对集的数据类型 PostgreSQL值。这在各种场景中都很有用,例如 具有许多很少检查的属性或半结构化属性的行 数据。键和值只是文本字符串
当然,您必须检查日期/时间戳如何转换成这种类型,并查看它是否适合您。您可以使用两种不同的技术:
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' |
+-------------+---------------+
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' |
+-------------+----------------+---------------+
所有这些只是为了总的想法。您应该根据自己的需要对其进行调整。另一种选择,取决于您所做的工作,可能是只使用一个值列,但在值周围存储一些json 这可能看起来像:
{
"type": "datetime",
"value": "2019-05-31 13:51:36"
}
甚至可以更进一步,使用or列。这就是为什么使用EAV表存储数据通常是个坏主意的原因之一。我同意。我想,继承现有设计的乐趣。这就是生活,波西米亚人,谢谢你的回复。如果使用选项2,我将如何对类型标记为日期/时间类型的值执行日期范围类型查询?最好编写一个与postgresql等价的存储过程来执行这种类型的查询?将日期存储为1979年以来的天数,或者如果是时间戳类型,则存储为1970年以来的秒数。谷歌大纪元时间或unix时间。回答不错,正是这个问题,我有,不能决定。即使在我的例子中,“postgres”非常有效地存储“null”值,但您对单个类型的论证也让我信服。谢谢选项2中范围查询的索引如何?我认为使用选项2,你最终会得到一些你无法有效查询的东西。。。