使用更大的运算符在 jsonb 数组中搜索嵌套值

Ryu*_*Ryu 3 postgresql performance index-tuning json postgresql-10 postgresql-performance

这是表定义(简化):

CREATE TABLE documents (
    document_id int4 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    data_block jsonb NULL
);
Run Code Online (Sandbox Code Playgroud)

示例值:

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}}
     ]}')
 , (977656, 
    '{"COMMONS": {"DATE": {"value": "2018-03-11"}},
     "PAYABLE_INVOICE_LINES": [
         {"AMOUNT": {"value": 555.10}}
     ]}');
Run Code Online (Sandbox Code Playgroud)

我想搜索其中一个'PAYABLE_INVOICE_LINES'元素包含'value'大于 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
Run Code Online (Sandbox Code Playgroud)

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

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

我尝试在 jsonb 对象的数组上添加 GIN 索引。但它似乎仅在使用 jsonb 运算符(如@>.

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

Erw*_*ter 9

这通常很难优化:jsonb这种测试没有直接的运算符或索引支持。

EXISTS至少应该比您拥有的更快,同时还避免重复行(多个数组元素匹配)和结果中的附加(冗余)列pil

SELECT *
FROM   documents d
WHERE  EXISTS (
   SELECT FROM jsonb_array_elements(d.data_block -> 'PAYABLE_INVOICE_LINES') pil 
   WHERE (pil->'AMOUNT'->>'value')::decimal > 1000
   );
Run Code Online (Sandbox Code Playgroud)

有关的:

为了使这个速度快几个数量级,提取每行的最大值并冗余保存它或IMMUTABLE在一个非常小和快速(但也是专门的)表达式索引中使用一个函数:

CREATE OR REPLACE FUNCTION f_doc_max_amout(jsonb)
  RETURNS numeric AS
$func$
   SELECT max((a->'AMOUNT'->>'value')::numeric)
   FROM   jsonb_array_elements($1) a
$func$ LANGUAGE sql IMMUTABLE;

CREATE INDEX documents_max_amount_idx
ON documents (f_doc_max_amout(data_block -> 'PAYABLE_INVOICE_LINES')); 
Run Code Online (Sandbox Code Playgroud)

查询(必须匹配索引表达式):

SELECT *
FROM   documents d
WHERE  f_doc_max_amout(data_block -> 'PAYABLE_INVOICE_LINES') > 1000;
Run Code Online (Sandbox Code Playgroud)

dbfiddle在这里

  • 如果可以在 Postgres 服务器上安装其他扩展,则有一个名为“jsquery”的特殊扩展,它扩展了常规 JSON 功能,并支持 GIN 索引:https://github.com/postgrespro/jsquery。适用于 Ubuntu 的软件包以及 RPM 均可用。 (2认同)