用于将自定义查询映射到具有用户定义的键/值对的分层实体的“规范”方法

Posted

技术标签:

【中文标题】用于将自定义查询映射到具有用户定义的键/值对的分层实体的“规范”方法【英文标题】:"Canonical" approach for mapping custom queries to hierarchical entities with user-defined key/value pairs 【发布时间】:2016-03-26 19:10:20 【问题描述】:

到目前为止,在我从事过的几乎所有基于 SQL 的数据库应用程序中,迟早都会出现以下三个方面的需求:

有一些实体以分层方式链接(即元组形成树结构)。 用户必须能够使用元组的值定义任意数量的自定义属性,并且这些值被继承/覆盖到树结构的叶子。 (“哑”属性通常就足够了。也就是说,没有唯一性约束,没有外键,每个属性只有一个值,......) 用户必须能够对此数据运行任意查询(即自定义布尔表达式,基于与 AND/OR 链接的用户定义属性值的过滤器)。

存储数据,大致匹配上面的前两个项目符号,非常简单:

层次结构是通过给相应的表一个 parent 列来建立的。对于根节点,此列将是 null,对于所有其他节点,该列将是指向父节点 ID 的指针。 用户自定义属性按照entity-attribute-value pattern存储。

虽然有许多资源建议使用不同的方法,尤其是在后一点(例如 answers here、here 或 here),但我通常无法摆脱传统的静态关系数据库模式。因此,让我们简单地假设上述内容是给定的。此外,我几乎不能依赖特定 DBMS 的细节。更常见的情况是系统应该与 MS SQL Server、Oracle 和可能的其他后端一起工作,而不需要两个明显不同的产品版本。

然而,解决第三项总是有问题的(即使不考虑属性值的层次继承)。连接数取决于布尔表达式中考虑的不同属性数。或者,通过确定在自定义布尔表达式的任何情况下考虑的不同属性的最大数量,可以在一定程度上减少连接的数量,这可以节省连接,但会使生成的查询和用于生成它们的代码更加难以理解和维护.例如,

a = 5 or (b = 8 and c = 9)

可以对属性值表进行 2 次连接。

我总是能够“以某种方式”做到这一点,但由于这似乎是一种相当普遍的情况,我正在寻找在这种情况下生成 SQL 查询的“规范”方式。这里有“标准模式”可以遵循吗?

【问题讨论】:

【参考方案1】:

小心不要成为内部平台效应的牺牲品。这是一个复杂的问题,SQL 本身就是为了处理复杂性而设计的。生成 DDL 以根据需要添加和删除列,并为查询生成简单的选择语句。将每个元组类型(不同的属性集)存储为一个表格。

关于继承,我建议在应用程序或 DAL 中处理它,并且只存储非继承的值。检索时,读取所有父行以计算功能值。如果您确实需要从 SQL 访问“功能”值,请使用索引视图或触发器将它们与存储区分开。

层次结构可以如您所描述的那样表示,但是一个简单的“父”列可能会使查询超出单个级别变得困难。查看 SQL Server 上的 hierarchyid 或 oracle 上的 CONNECT BY

避免 EAV 存储允许您:

在需要时使用索引和统计信息 保持高效存储(ints 存储为ints,money 存储为money) 保留可理解的查询 (SELECT * FROM vwProducts WHERE Color = 'RED' ORDER BY Price ASC)

如果你想要一个 EAV 系统,因为你有太多的属性(每种类型>1024)或者它们不是静态定义的(每小时有很多变化),我会首先避免使用关系数据库。请改用 EAV (NoSQL) 数据库服务器。

tl;dr:如果你有一个模式,使用 DDL 告诉服务器它。如果不这样做,请使用更合适的服务器。

【讨论】:

“将每个元组类型(不同的属性集)存储为一个表。” - 我不知道如何结合这个建议和限制“我通常无法摆脱传统的静态关系数据库模式。因此,让我们简单地假设上述内容是给定的。”问题中说明。不过,关于层次结构的建议听起来很有帮助;谢谢你。 @O.R.Mapper,规范的方法是使用设计的数据库服务器。将任何东西作为 EAV 存储在基于表的数据库服务器中无疑是一种 hack。允许用户添加任意​​属性正是动态模式的定义。为什么需要静态架构? 不想要静态模式。不幸的是,项目中负责 DB 管理的人员会告诉我,我得到的正是我提前订购的桌子,而且他们得到了严格拒绝的上级的支持。任何运行应用程序的用户都可能被允许有权更改有关架构的任何内容。 @O.R.Mapper,您始终可以为用户定义的查询提供专用架构。这实际上是一个教育客户使用正确工具来完成工作的问题。您要求做的事情既不明智也不“规范”。他们强制使用静态模式的原因是什么?向 DBA 展示他们在这两种情况下必须处理的查询和模式通常很有帮助,我还没有遇到过更喜欢 EAV 的人。 “他们强制使用静态模式的原因是什么?” - 在最近的一个例子中......决定该项目的人 (a) 是 SQL 的绝对粉丝,(b) 很高兴刚刚从使用对象数据库的失败实验中恢复过来,并认为“对象数据库”是一种禁忌因为,(c) 确信 WHERE EXISTS (SELECT '0' FROM KeyValueTable x WHERE x.y = 42)) 构造同样快并且避免连接,(d) 完全反对授予任何用户模式编辑权限,即使对于单个表也是如此,并且 (e) 确信动态添加列是由于频繁......不切实际......

以上是关于用于将自定义查询映射到具有用户定义的键/值对的分层实体的“规范”方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 keycloak 将自定义键/值添加到 JWT 令牌有效负载或用户

如何为具有不同数量的键/值对的对象添加类型? [关闭]

如何动态添加solr中的键值对?

Go 语言入门很简单 -- 8. Go Maps #私藏项目实操分享#

将自定义JavaScript模型映射到具有observable的Knockout模型

JS基础 Map是一组键值对的结构