我尝试搜索解决方案,但我找不到任何针对我案例的内容......
这是数据库声明(简化):
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万。
你有什么想法可以获得更好的表现吗?
由于
答案 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:通常limit
与order 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列要快得多。