用于过滤 JSONB 数组中所有数组项满足条件的 SQL 查询

发布于 2025-01-10 20:20:00 字数 1345 浏览 4 评论 0原文

我以前发过类似的帖子,但由于上下文错误而删除了它。

我的数据库中的一个表包含一个 JSONB 列,其中包含一组 JSON 对象。它与我在下面模拟的这个 session 表示例没有什么不同。

iduser_idsnapshotinsert_at
137{购物车:[{product_id:1,price_in_cents:3000,name:“产品A”},{product_id:2,price_in_cents:2500,name:“产品B”}]}2022-01-01 20:00:00.000000
224{购物车: [{product_id:1,price_in_cents:3000,名称:“产品 A”},{product_id:3,price_in_cents:5500,名称:“产品 C”}]}2022-01-02 20:00:00.000000
388{cart :[{product_id:4,price_in_cents:1500, name: "product D"}, {product_id: 2, Price_in_cents: 2500, name: "product B"}]}2022-01-03 20:00:00.000000

我用来从此表中检索记录的查询是如下。

SELECT sessions.*
FROM sessions
INNER JOIN LATERAL (
    SELECT *
    FROM jsonb_to_recordset(sessions.snapshot->'cart')
    AS product(
        "product_id" integer,
        "name" varchar,
        "price_in_cents" integer
    )
) AS cart ON true;

我一直在尝试更新上面的查询,以仅检索会话表中购物车中所有产品的 price_in_cents 值大于 <代码>2000。

到目前为止,我在形成这个查询方面还没有取得任何成功,但如果这里有人能给我指出正确的方向,我将不胜感激。

I made a similar post before, but deleted it as it had contextual errors.

One of the tables in my database includes a JSONB column which includes an array of JSON objects. It's not dissimilar to this example of a session table which I've mocked up below.

iduser_idsnapshotinserted_at
137{cart: [{product_id: 1, price_in_cents: 3000, name: "product A"}, {product_id: 2, price_in_cents: 2500, name: "product B"}]}2022-01-01 20:00:00.000000
224{cart: [{product_id: 1, price_in_cents: 3000, name: "product A"}, {product_id: 3, price_in_cents: 5500, name: "product C"}]}2022-01-02 20:00:00.000000
388{cart: [{product_id: 4, price_in_cents: 1500, name: "product D"}, {product_id: 2, price_in_cents: 2500, name: "product B"}]}2022-01-03 20:00:00.000000

The query I've worked with to retrieve records from this table is as follows.

SELECT sessions.*
FROM sessions
INNER JOIN LATERAL (
    SELECT *
    FROM jsonb_to_recordset(sessions.snapshot->'cart')
    AS product(
        "product_id" integer,
        "name" varchar,
        "price_in_cents" integer
    )
) AS cart ON true;

I've been trying to update the query above to retrieve only the records in the sessions table for which ALL of the products in the cart have a price_in_cents value of greater than 2000.

To this point, I've not had any success on forming this query but I'd be grateful if anyone here can point me in the right direction.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

感受沵的脚步 2025-01-17 20:20:00

您可以使用 JSON 路径表达式:

select *
from sessions
  ...
where not sessions.snapshot @@ '$.cart[*].price_in_cents <= 2000'

没有 JSON 路径表达式可以检查所有数组元素是否大于 2000。因此,这将返回没有元素小于 2000 的那些行 - 因为可以使用 JSON 路径表达式来表达。

You can use a JSON path expression:

select *
from sessions
  ...
where not sessions.snapshot @@ '$.cart[*].price_in_cents <= 2000'

There is no JSON path expression that would check that all array elements are greater 2000. So this returns those rows where no element is smaller than 2000 - because that can be expressed with a JSON path expression.

牵强ㄟ 2025-01-17 20:20:00

这是基于您原始查询的想法的一种可能的解决方案。
购物车 JSON 数组对象的每个元素都连接到其 sessions 父行。既然所需的 JSON 数组元素已公开,您就需要添加 WHERE 子句条件。

SELECT *
FROM (
  SELECT
    sess.id,
    sess.user_id,
    sess.inserted_at,
    cart_items.cart_name,
    cart_items.cart_product_id,
    cart_items.cart_price_in_cents
  FROM sessions sess,
    LATERAL (SELECT (snapshot -> 'cart') snapshot_cart FROM sessions WHERE id = sess.id) snap_arr,
    LATERAL (SELECT
               (value::jsonb ->> 'name')::text cart_name,
               (value::jsonb -> 'product_id')::int cart_product_id,
               (value::jsonb -> 'price_in_cents')::int cart_price_in_cents
             FROM JSONB_ARRAY_ELEMENTS(snap_arr.snapshot_cart)) cart_items
) session_snapshot_cart_product;

解释:

  • sessions 表中,提取 cart 数组,并按 sessions 行连接
  • cart 的必要项目> 然后,使用 JSONB_ARRAY_ELEMENTS(jsonb) 函数通过第二个连接解除 JSON 数组的嵌套

Here is one possible solution based on the idea of your original query.
Each element of the cart JSON array object is joined to its sessions parent row. You 're left adding the WHERE clause conditions now that the wanted JSON array elements are exposed.

SELECT *
FROM (
  SELECT
    sess.id,
    sess.user_id,
    sess.inserted_at,
    cart_items.cart_name,
    cart_items.cart_product_id,
    cart_items.cart_price_in_cents
  FROM sessions sess,
    LATERAL (SELECT (snapshot -> 'cart') snapshot_cart FROM sessions WHERE id = sess.id) snap_arr,
    LATERAL (SELECT
               (value::jsonb ->> 'name')::text cart_name,
               (value::jsonb -> 'product_id')::int cart_product_id,
               (value::jsonb -> 'price_in_cents')::int cart_price_in_cents
             FROM JSONB_ARRAY_ELEMENTS(snap_arr.snapshot_cart)) cart_items
) session_snapshot_cart_product;

Explanation :

  • From the sessions table, the cart array is exctracted and joined per sessions row
  • The necessary items of the cart JSON array is then unnested by the second join using the JSONB_ARRAY_ELEMENTS(jsonb) function
伪装你 2025-01-17 20:20:00

以下内容对我来说效果很好,并允许我灵活地使用不同的比较运算符,而不仅仅是 ==<= 等运算符。

在我需要构建的一个场景中,我还需要使用 IN 比较运算符将子查询中的 WHERE 与值数组进行比较,这不是使用我们研究过的一些其他解决方案是可行的。

将其留在这里,以防其他人遇到与我相同的问题,或者其他人找到更好的解决方案或想要在此基础上提出建议。

SELECT *
FROM sessions
WHERE NOT EXISTS (
    SELECT sessions.*
    FROM sessions
    INNER JOIN LATERAL (
        SELECT *
        FROM jsonb_to_recordset(sessions.snapshot->'cart')
        AS product(
            "product_id" integer,
            "name" varchar,
            "price_in_cents" integer
        )
    ) AS cart ON true
    WHERE name ILIKE "Product%";
)

The following worked well for me and allowed me the flexibility to use different comparison operators other than just ones such as == or <=.

In one of the scenarios I needed to construct, I also needed to have my WHERE in the subquery also compare against an array of values using the IN comparison operator, which was not viable using some of the other solutions that were looked at.

Leaving this here in case others run into the same issue as I did, or if others find better solutions or want to propose suggestions to build upon this one.

SELECT *
FROM sessions
WHERE NOT EXISTS (
    SELECT sessions.*
    FROM sessions
    INNER JOIN LATERAL (
        SELECT *
        FROM jsonb_to_recordset(sessions.snapshot->'cart')
        AS product(
            "product_id" integer,
            "name" varchar,
            "price_in_cents" integer
        )
    ) AS cart ON true
    WHERE name ILIKE "Product%";
)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文