【问题标题】:Postgres Querying/filtering JSONB with nested arraysPostgres 使用嵌套数组查询/过滤 JSONB
【发布时间】:2022-01-02 14:02:09
【问题描述】:

以下是我的示例要求

我想要满足以下所有条件的客户

  1. 在“xyz”国家/地区,于 2019 年至 2021 年间注册。
  2. 应该至少有一个账户余额在 10000 到 13000 之间,分支是“abc”,交易日期在 20200110 到 20210625 之间。它被格式化并存储为数字
  3. 应至少有一个地址处于“state1”状态,并且 pin 码介于 625001 和 625015 之间

下面是表结构

        CREATE TABLE customer_search_ms.customer
        (
            customer_id integer,
            customer_details jsonb
        )
    

表中可能有数百万行。 我在 customer_details 列上创建了 jsonb_ops 类型的 GIN 索引,因为我们还将检查存在条件和范围比较

以下是 customer_data JSONB 列中的示例数据

customer_id : 1

    {
        "customer_data": {
            "name": "abc",
            "incorporated_year": 2020,
            "country":"xyz",
            "account_details": [
                {
                    "transaction_dates": [
                        20180125, 20190125, 20200125,20200525
                    ],
                    "account_id": 1016084,
                    "account_balance": 2000,
                    "account_branch": "xyz"
                },
                {
                    "transaction_dates": [
                        20180125, 20190125, 20200125
                    ],
                    "account_id": 1016087,
                    "account_balance": 12010,
                    "account_branch": "abc"
                }
            ],
            "address": [
                {
                    "address_id": 24739,
                    "door_no": 4686467,
                    "street_name":"street1",
                    "city": "city1",
                    "state": "state1",
                    "pin_code": 625001
                },
                {
                    "address_id": 24730,
                    "door_no": 4686442,
                    "street_name":"street2",
                    "city": "city1",
                    "state": "state1",
                    "pin_code": 625014
                }
            ]
        }
    }

现在我上面写的查询是

SELECT  c.customer_id,
        c.customer_details
FROM customer_search_ms.customer c
WHERE c.customer_details @@ CAST('$.customer_data.country ==  "xyz" && $.customer_data.incorporated_year >= 2019 && $.customer_data.incorporated_year <= 2021 ' AS JSONPATH)
AND c.customer_details @? CAST('$.customer_data.account_details[*] ? (@.account_balance >=  10000) ? (@.account_balance <=  13000) ?(@.account_branch ==  "abc") ? (@.transaction_dates >=  20200110) ? (@.transaction_dates <=  20210625)' AS JSONPATH)
AND c.customer_details @? CAST('$.customer_data.address[*] ? (@.state ==  "state1") ? (@.pin_code >=  625001) ? (@.pin_code <= 625015)  ' AS JSONPATH)

处理上述情况是最好的写作方式。是否可以将所有 3 个条件(客户/帐户/地址)组合成一个表达式?该表将有数百万行。 我认为将其作为一种表达方式并点击数据库将提供最佳性能。是否可以将这三个条件组合为一个表达式

【问题讨论】:

  • 使用适当的规范化数据模型会容易得多。
  • 您的示例数据格式错误,第二个街道名称前缺少逗号。
  • 你显示的查询没有给我你显示的错误。

标签: postgresql query-optimization database-performance jsonb


【解决方案1】:

您的查询没有给我您报告的错误。相反,它运行,但与你想要的相比,确实给出了“错误”的结果。其中有几个错误不是语法错误,而是给出错误的结果。

您的第一个 jsonpath 看起来不错。它是一个布尔表达式,@@ 检查该表达式是否产生 true

您的第二个 jsonpath 有两个问题。它会生成与您的条件匹配的对象列表。但是对象不是布尔值,所以@@ 会不高兴,返回SQL NULL,这里和false 一样处理。相反,您需要测试该列表是否为空。这就是@? 所做的,所以使用它来代替@@。此外,您的日期存储为 8 位整数,但您将它们与 8 个字符的字符串进行比较。在 jsonpath 中,这样的跨类型比较会产生 JSON null,这里将其视为 false。因此,您要么需要将存储更改为字符串,要么将与它们进行比较的文字更改为整数。

您的第三个 jsonpath 也有 @@ 问题。它与类型问题相反,您将 pin_code 存储为字符串,但您正在针对整数测试它们。最后,您有一次出现“pin_code”拼写错误。

【讨论】:

  • 谢谢简斯。我已经更正了原始帖子中的代码和数据。由于机密性,我不得不发布熟数据并在这方面犯了错误。我无法重现错误场景。对于上面给出的查询,在 where 子句中有 3 个条件是否有更好的方法。我的想法是,如果我能够将其作为单一条件而不是 3,它会更好。任何指导都会对我有很大帮助。谢谢
猜你喜欢
  • 2023-04-07
  • 1970-01-01
  • 2019-03-14
  • 1970-01-01
  • 2021-02-18
  • 2017-01-28
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多