需要一种在 SQL 数据库中存储/查询 json 的有效方法

Posted

技术标签:

【中文标题】需要一种在 SQL 数据库中存储/查询 json 的有效方法【英文标题】:Need an efficient way to store/query json in a SQL database 【发布时间】:2012-06-26 21:03:12 【问题描述】:

我正在实施一项服务,其中每个用户都必须拥有自己的 json/文档数据库。除了让用户通过示例查询 json 文档之外,数据库还必须支持涉及多个文档的 ACID 事务,所以我放弃了使用 Couch/Mongo 或其他 NoSQL 数据库(不能使用 RavenDB,因为它必须在 Unix 系统上运行)。

考虑到这一点,我一直试图想办法在 SQL 数据库之上实现它。到目前为止,这是我想出的:

CREATE TABLE documents (
  id INTEGER PRIMARY KEY,
  doc TEXT
);

CREATE TABLE indexes (
  id INTEGER PRIMARY KEY,
  property TEXT,
  value TEXT,
  document_id INTEGER
)

每个用户都有一个包含这两个表的数据库,并且用户必须声明他需要查询哪些字段,以便系统可以正确填充“索引”表。因此,如果用户“A”将其帐户配置为启用按“姓名”和“年龄”进行查询,则每次该用户插入具有“姓名”或“年龄”属性的文档时,系统也会向“索引”插入一条记录表,其中 'property' 列将包含 name/age , 'value' 将包含属性值, 'document_id' 将指向相应的文档。

例如,假设用户插入以下文档:

'"name" : "Foo", "age" 43'

这将导致对“文档”表的插入和对“索引”表的另外两个插入:

INSERT INTO documents (id,doc) VALUES (1, '"name" : "Foo", "age" 43');
INSERT INTO indexes (property, value, document_id) VALUES ('name', 'foo', 1);
INSERT INTO indexes (property, value, document_id) VALUES ('age', '43', 1);

假设用户“A”向服务发送了以下查询:

'"name": "Foo", "age": 43' //(the queries are also json documents).

此查询将被转换为以下 SQL:

SELECT doc FROM documents
WHERE id IN (SELECT document_id FROM indexes
             WHERE document_id IN (SELECT document_id FROM indexes
                                   WHERE property = 'name' AND value = 'Foo')
             AND property = 'age' AND value = '43') 

我的问题:

知道用户可能能够在他的查询中使用大量条件(比如说 20-30 个 AND 条件),这会导致子查询嵌套非常高,上面的 SELECT 查询在大多数情况下的效率如何数据库系统(postgres、mysql...)? 对于最终将包含数百万/数十亿 json 文档的数据库,上述解决方案是否可行? 有没有更好的方法来满足我的要求? 是否有可扩展的文档数据库,可以执行涉及多个文档的 ACID 事务并在 Unix 系统上运行?

【问题讨论】:

PostgreSQL 9.2 将支持 JSON 数据类型,并且通过一些函数(例如用 javascript 编写),上述内容应该是可能的。示例见此处:people.planetpostgresql.org/andrew/index.php?/archives/… 看看 CouchDB 是否适合你:“CouchDB 提供 ACID 语义。它通过实现一种多版本并发控制的形式来做到这一点,这意味着 CouchDB 可以处理大量并发读者和作者,而无需冲突。” 关于 PostgreSQL 的有趣提示,我去看看,谢谢 Dmitriy,CouchDB 仅在单个文档操作上是 ACID 【参考方案1】:

您的indexes 表是所谓的Entity-Attribute-Value

EAV 表非常适合存储信息并在您知道实体时调用它。 (在您的情况下,当您知道 document_id 时,查找所有 indexes 行。)

但它们可怕反过来:提供属性-值组合来搜索实体。这正是您在最终查询中所拥有的。随着越来越多的实体共享相同的属性值组合(例如name=foo,查询性能会下降。

所以,回答你的前两个问题: 1. 所写的查询在搜索n 属性时需要n 子查询。随着 n 的增长,这将非常糟糕。 2. 随着记录数量的增加,它会下降,尤其是数百万/十亿条记录。

一般来说,如果你读到EAV,人们强烈建议你避开它。

而且,更糟糕的是,在 SQL 中并没有真正好的替代方案。优化搜索的标准方法是使用索引,它可以很容易地建模为排序数据集。但是你需要很多索引: - 如果您搜索所有三列,(fieldX, fieldY, fieldZ) 上的索引很棒。 - 但是,如果您必须在 just fieldZ 上搜索,那很糟糕

如果您可以使用具有固定列数的传统表格重新建模,并且有空间应用您需要的每个索引组合,那将是您最高效的模型。

如果您无法修复列数(新的properties 一直出现) 和/或您没有空间容纳所有不同的索引组合,您似乎被 EAV 卡住。这会起作用,但就“即时”结果而言,它不会很好地扩展。

注意:如果您坚持使用 EAV,您是否测试过这种查询结构?

  SELECT
    document_id
  FROM
    indexes
  WHERE
       (property = 'name' AND value = 'Foo')
    OR (property = 'age'  AND value = '43' )
  GROUP BY
    document_id
  HAVING
    COUNT(*) = 2

这假定(document_id, property, value) 是唯一的。否则一个文档可能有两次('name', 'foo'),因此通过COUNT(*) 子句。

【讨论】:

我不认为'indexes'表是使用'Entity-Attribute-Value'方法对数据建模,它只是一种在'documents'表中'手动'索引无模式数据的方法.我忘了提到 name 和 value 列也会被索引,你不认为这会使查询运行得更快吗? @ThiadodeArruda - 不幸的是,它正是 EAV。您的DocumentsEntities。您的PropertiesAttributes。而你的Values 是,嗯,我想你明白这一点。与不这样做相比,索引(property, value, document_id) 肯定会改善一些事情,但这是最低限度的工作假设。你仍然有 EAV 的所有困难。它总是比“传统”表慢得多。并且对于任何给定属性共享相同值的记录越多,它就会变得越慢。而且你搜索的属性越多,速度就越慢。

以上是关于需要一种在 SQL 数据库中存储/查询 json 的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS:从 SQL 查询中获取 JSON

MySQL存储过程

mysql存储过程。。

T-SQL基础之简单查询

需要一种在 vueJS 中启用 CORS 的方法 [重复]

怎样在java中查询mysql得到如下的json格式的结果