Postgres jsonb在数组中搜索更大的运算符(使用jsonb_array_elements)

时间:2018-03-31 10:23:29

标签: postgresql jsonb

我尝试搜索解决方案,但我找不到任何针对我案例的内容......

这是数据库声明(简化):

CREATE TABLE documents (
    document_id int4 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    data_block jsonb NULL
);

这是插入的一个例子。

INSERT INTO documents (document_id, data_block)
VALUES(878979, 
    {"COMMONS": {"DATE": {"value": "2017-03-11"}},
     "PAYABLE_INVOICE_LINES": [
         {"AMOUNT": {"value": 52408.53}}, 
         {"AMOUNT": {"value": 654.23}}
     ]});
INSERT INTO documents (document_id, data_block)
VALUES(977656, 
    {"COMMONS": {"DATE": {"value": "2018-03-11"}},
     "PAYABLE_INVOICE_LINES": [
         {"AMOUNT": {"value": 555.10}}
     ]});

我想搜索其中一个PAYABLE_INVOICE_LINES有一行值大于1000.00的所有文件

我的查询是

select *
from documents d
cross join lateral jsonb_array_elements(d.data_block -> 'PAYABLE_INVOICE_LINES') as pil 
where (pil->'AMOUNT'->>'value')::decimal >= 1000

但是,由于我想限制为50个文档,我必须对document_id进行分组并将结果限制为50个。

拥有数百万个文档,这个查询非常昂贵... 10秒,100万。

你有什么想法可以获得更好的表现吗?

由于

2 个答案:

答案 0 :(得分:2)

而不是cross join lateral使用where exists

select *
from documents d
where exists (
  select 1
  from jsonb_array_elements(d.data_block -> 'PAYABLE_INVOICE_LINES') as pil
  where (pil->'AMOUNT'->>'value')::decimal >= 1000)
limit 50;

<强>更新

还有另一种方法,更复杂但也更有效。

创建从JSONB数据中返回最大值的函数,如下所示:

create function fn_get_max_PAYABLE_INVOICE_LINES_value(JSONB) returns decimal language sql as $$
  select max((pil->'AMOUNT'->>'value')::decimal)
  from jsonb_array_elements($1 -> 'PAYABLE_INVOICE_LINES') as pil $$

在此功能上创建索引:

create index idx_max_PAYABLE_INVOICE_LINES_value
  on documents(fn_get_max_PAYABLE_INVOICE_LINES_value(data_block));

在查询中使用函数:

select *
from documents d
where fn_get_max_PAYABLE_INVOICE_LINES_value(data_block) > 1000
limit 50;

在这种情况下,将使用索引,并且对大量数据的查询速度会快得多。

PS:通常limitorder by成对有意义。

答案 1 :(得分:1)

分组和限制很容易:

select  document_id
from    documents d
cross join lateral 
        jsonb_array_elements(d.data_block -> 'PAYABLE_INVOICE_LINES') as pil 
where   (pil->'AMOUNT'->>'value')::decimal >= 1000
group by
        document_id
limit   50

如果您更频繁地查询,可以将文档列表和发票行存储在单独的表中。当您添加,修改或删除文档时,您还必须使单独的表保持最新。但是查询常规表比查询JSON列要快得多。