在网格中存储分层对象
Posted
技术标签:
【中文标题】在网格中存储分层对象【英文标题】:Storing layered objects in a grid 【发布时间】:2021-12-31 16:09:15 【问题描述】:假设我有一个画布,其中可以添加各种对象,例如 a/an:
绘图 图片 图表 注意 表对于每个对象,我需要存储尺寸和层顺序,例如:
对象ID 图层索引 尺寸((x1,y1),(x2,y2))每个对象都有非常不同的属性,因此存储在不同的表(或类或其他)中。是否可以将其存储到关系数据库中,如果可以,怎么做?在 JSON 中会是这样的:
// LayerIndex is the ArrayIndex
// No need to store ObjectID, since the object is stored within the array itself
Layers = [
Type: Drawing, Props: <DrawingPropertyObj>, Dimensions: [(1,2), (3,4)],
Type: Chart, Props: <ChartPropertyObj>, Dimensions: [(3,4), (10,4)],
Type: Table, Props: <TablePropertyObj>, Dimensions: [(10,20), (30,44)],
...
]
我想到的一个选项是将 FK 存储到每个表中,但在这种情况下,我可能会将其加入到每个对象类型的 N 个不同表中,因此如果有 100 个对象类型,...
【问题讨论】:
entity–attribute–value model 可以为您的网格对象存储不同的属性。 这在this线程和其中的链接线程中得到了充分的回答 需要通过对象属性查询db吗? 这是一个常见的副本。 How can you represent inheritance in a database?等How much research effort is expected of Stack Overflow users? @philipxy 明白了。我认为您看到该问题具有标签sql-server
(以及您从该问题中标记的重复项),是吗?除了一个完整的字面意思是Alternatively, consider using a document databases (such as MongoDB) which natively support rich data structures and nesting.
的单句答案之外,没有任何答案涉及潜在的非关系性方式。
【参考方案1】:
您有很多选择,如下所示。
你选择哪一个没有太大区别,但我会避免你所说的多表设计。具有 100 个属性的对象类型将分散在 101 个表中而没有任何好处。每种正在读取的对象类型有 101 次磁盘页面访问。这是不必要的(如果这些页面被缓存,那么这个问题会比其他问题少,但仍然是浪费)。
如果您不希望过滤诸如“所有颜色为红色的对象”之类的内容,即使双表也不是必需的,但我想性能并不是那么迫切需要达到这一点,其他事情更重要,或者其他瓶颈对性能的影响更大,因此请选择最适合您的单表。
单表 - 每个对象类型的灵活架构
objlayerindex | type | props | x0 | y0 | x1 | y1 |
---|---|---|---|---|---|---|
0 | drawing | color:#00FF00,background-color:#00FFFF | 1 | 2 | 3 | 4 |
1 | chart | title:2021_sales,values:[[0,0],[3,4]] | 11 | 22 | 33 | 44 |
单表 - 每个对象类型的固定架构
objlayerindex | type | props | x0 | y0 | x1 | y1 |
---|---|---|---|---|---|---|
0 | drawing | #00FF00,#00FFFF | 1 | 2 | 3 | 4 |
1 | chart | 2021_sales,"[[0,0],[3,4]]" | 11 | 22 | 33 | 44 |
双桌
主要
objlayerindex | type | x0 | y0 | x1 | y1 |
---|---|---|---|---|---|
0 | drawing | 1 | 2 | 3 | 4 |
1 | chart | 11 | 22 | 33 | 44 |
属性
objlayerindex | propertyname | propertyvalue |
---|---|---|
0 | color | #00FF00 |
0 | background-color | #00FFFF |
1 | title | 2021_sales |
1 | values | [[0,0],[3,4]] |
propertyindex
。对于喜欢标准化的人,也可以将propertyname
从此表中取出到propertykey-propertydescription
并通过其propertykey
引用。
多表
主要
objlayerindex | type | x0 | y0 | x1 | y1 |
---|---|---|---|---|---|
0 | drawing | 1 | 2 | 3 | 4 |
1 | chart | 11 | 22 | 33 | 44 |
颜色
objlayerindex | colorcode |
---|---|
0 | #00FF00 |
背景色
objlayerindex | colorcode |
---|---|
0 | #00FFFF |
标题
objlayerindex | title |
---|---|
1 | 2021_sales |
价值观
objilayerindex | chart |
---|---|
1 | [[0,0],[3,4]] |
价值观
objlayerindex | datapoint | x | y |
---|---|---|---|
1 | 0 | 0 | 0 |
1 | 1 | 3 | 4 |
您也可以使用非关系格式。
文档(Json)存储
[
type:drawing,props:color:#00FF0,background-color:#00FF0,position:[1,2,3,4],
type:chart,props:title:2021_sales,values:[[0,0],[3,4]],position:[11,22,33,44]
]
我们在这里引用它是因为它是一种流行且简单的格式,但可以使用不同的编码来代替 JSON(CSV、protocolbuffers、avro 等)
【讨论】:
【参考方案2】:“严格”的关系数据库不适合这项任务,因为您必须选择:
-
每种对象类型都有不同的表,其中每个属性的列适用于该特定对象类型
所有对象类型的单个表,每个属性都有列,其中大部分不用于任何给定的对象类型
一个子表,每个属性一行
在转向一个好的通用解决方案之前,让我们讨论一下:
1。每种对象类型都有不同的表
这是一个非首发。问题是:
维护成本高:每次向应用添加新对象类型时都必须创建一个新表 痛苦的查询:您必须连接到每个表,或者水平连接 - 每个表连接到一个非常长的行中,或者垂直连接到一系列联合连接中,导致 sparse array(参见选项 2)2。所有对象类型的单个表
虽然您正在处理sparse array,但如果大多数对象类型使用大多数属性(即,它不是 稀疏的),这是一个不错的选择。但是,如果您的域中不同属性的数量很多,并且/或者大多数属性并非被所有类型使用,那么您必须在引入新类型时添加列,这虽然比添加表更好,但仍然需要更改架构对于新类型 = 高维护率
3。子表
这是经典的方法,但使用起来更糟,因为您要么必须运行单独的查询来收集每个对象的所有属性(慢,高维护),要么单独编写针对每种对象类型进行查询,为每个属性加入一次子表,以将多个行扁平化为每个对象的一行,有效地导致选项 1,但编写查询的维护成本更高
这些都不是很好的选择。你想要的是:
每个对象一行 简单查询 简单架构 维护成本低document database,例如Elasticsearch 可以为您提供所有这些开箱即用的功能,但是您可以通过放宽“严格性”并将整个对象作为 json 保存在单个列中来实现与关系数据库相同的效果:
create table object (
id int, -- typically auto incrementing
-- FK to parent - see below
json text -- store object as json
);
顺便说一句,postgres 将是一个不错的选择,因为它通过 json
数据类型具有 native support for json。
在我的职业生涯中,我曾多次使用它,总是成功。我为对象类类型添加了一个列(在 java 上下文中):
create table object (
id int,
-- FK to parent - see below
class_name text,
json text
);
并使用 json 库将使用指定类的 json 反序列化为该类的对象。无论您使用哪种语言,都可以实现这一想法。
至于层次结构,关系数据库可以很好地做到这一点。从画布:
create table canvas (
id int,
-- various attributes
);
如果对象没有被重用:
create table object (
id int,
canvas_id int not null references canvas,
class_name text,
json text,
layer int not null
);
如果对象被重复使用:
如果对象没有被重用:
create table object (
id int,
class_name text,
json text
);
create table canvas_object (
canvas_id int not null references canvas,
object_id int not null references object,
layer int not null
);
【讨论】:
说到 postgres,这是一个简洁的小包/脚本,它利用了您提到的许多概念:github.com/solidsnack/pg-sql-variants @David542 你可以使用类层次结构,但我在尝试时总是束手无策。恕我直言,愚蠢的东西越容易使用,最终解决方案就越简单,即更好。如果它易于理解,则易于使用和维护。 您在开头提到A "strict" relational database doesn't suit this task well
,如果您的任务是存储这些数据,您会使用关系数据库还是非关系数据库?而且,如果是后者,您是否也希望将其作为一个部分包含在您的答案中?
我会使用关系,因为有额外的花里胡哨(例如事务边界、参照完整性、视图等),但是在“文档”样式中 - 这意味着它会违反 first normal form,因为存储整个实体的单个列。我已经使用 postgres、Lucene(实际上是 Solr)和 Elasticsearch 完成了它——一切正常。请注意,RDBMS 是 ACID,但文档数据库不是(它们是 eventually consistent,这可能不适合您的目的。以上是关于在网格中存储分层对象的主要内容,如果未能解决你的问题,请参考以下文章