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

SQL query to filter where all array items in JSONB array meet condition

我之前做了一个类似的post,但是因为它有上下文错误而删除了它。

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

id user_id snapshot inserted_at
1 37 {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
2 24 {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
3 88 {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

我用于从此 table 检索记录的查询如下。

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;

我一直在尝试更新上面的查询以仅检索会话 table 中的记录,其中 ALL 购物车中的产品具有 price_in_cents的值大于2000.

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

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

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

没有 JSON 路径表达式可以检查所有数组元素是否大于 2000。所以这个 returns 那些没有元素小于 2000 的行 - 因为可以用JSON 路径表达式。

根据您最初的查询想法,这是一种可能的解决方案。 购物车 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 table 中提取 cart 数组并按 sessions
  • 加入
  • cart JSON 数组的必要项然后由第二个连接使用 JSONB_ARRAY_ELEMENTS(jsonb) 函数
  • 取消嵌套

以下对我来说效果很好,让我可以灵活地使用不同的比较运算符,而不仅仅是 ==<=.

在我需要构建的场景之一中,我还需要让子查询中的 WHERE 也使用 IN 比较运算符与值数组进行比较,这是不可行的使用其他一些已查看的解决方案。

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

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%";
)