有没有办法对 jsonb 对象中的值列表应用条件?
Posted
技术标签:
【中文标题】有没有办法对 jsonb 对象中的值列表应用条件?【英文标题】:Is there a way to apply a conditional against a list of values in a jsonb object? 【发布时间】:2020-09-18 20:07:20 【问题描述】:目前,我在 PostgreSQL 表中有一个 jsonb 列,它存储格式为 json 的对象
"subscriptions":
"subscription1":
"subscribed": boolean
,
"subscription2":
"subscribed": boolean
可以有任意数量的订阅1/2/等。在文档中,都具有不同的名称。我正在尝试找出文档是否包含我拥有的每种订阅类型的 subscriptions.subscription1.subscribed == true 等。
我有一个我需要查询的订阅密钥列表,所以我的第一反应是遍历它们并查询数据库以获得这些计数:
subscriptionsKeys.forEach subscription ->
// find number of users where `subscription` == true
val queryResult = repository.getNumberOfUsersSubscribed(subscription)
// queryResult contains the number of users subscribed to `subscription`
其中 getNumberOfUsersSubscribed() 定义为:
@SqlQuery(
"""
SELECT count(*) as number_of_users FROM table
WHERE jsonb_extract_path_text(body,'subscriptions',:subscriptionKey,'subscribed') = 'true'
"""
)
fun getNumberOfUsersSubscribedToOptOutList(
subscriptionKey: String
): Int
这很好,但是对于大量订阅,这意味着我将在表上执行 count(*) n 次,在我们的生产环境中超过 60 次查询每个都处理超过 4500 万条记录。
我希望有某种方法可以构造一个 SQL/jsonb 查询,允许我传入一个键列表/数组 ['subscription1','subscription2',etc] 将应用上述 = true
条件针对每一行的输入列表,然后以以下示例格式返回结果:
+------------------------------------+
| subscription_key | number_of_users |
+------------------+-----------------+
| subscription1 | 6 |
| subscription2 | 59 |
| etc. | n |
+------------------+-----------------+
任何建议将不胜感激!
【问题讨论】:
【参考方案1】:只要您的库允许您将数组传递到 text[]
占位符,那么这应该可以工作。虽然它不会特别快,但它应该比您已经使用的方法更快。
with yourtable (id, body) as (
values (1, '
"subscriptions":
"subscription1":
"subscribed": true
,
"subscription2":
"subscribed": true
'::jsonb)
), search_terms as (
select *
from unnest(array['subscription1', 'subscription2']) as st(term)
)
select st.term, count(*)
from yourtable y
cross join lateral jsonb_each(y.body->'subscriptions') as b(k, v)
join search_terms st
on st.term = b.k
and b.v->>'subscribed' = 'true'
group by st.term
;
┌───────────────┬───────┐
│ term │ count │
├───────────────┼───────┤
│ subscription1 │ 1 │
│ subscription2 │ 1 │
└───────────────┴───────┘
(2 rows)
根据您的评论,如果您想将未明确将subscribed
显示为false
的订阅计数:
with yourtable (id, body) as (
values (1, '
"subscriptions":
"subscription1":
"subscribed": true
,
"subscription2":
"subscribed": true
,
"subscription3":
"subscribed": false
'::jsonb),
(2, '
"subscriptions":
"subscription1":
"subscribed": true
,
"subscription2":
"subscribed": true
,
"subscription3":
"subscribed": false
'::jsonb),
(3, ''::jsonb)
), search_terms as (
select *
from unnest(array['subscription1', 'subscription2', 'subscription3', 'subscription4']) as st(term)
)
select st.term,
count(*)
filter (where
coalesce(
body->'subscriptions'
->st.term
->>'subscribed', ''
) != 'false')
from yourtable t
cross join search_terms st
group by st.term
order by st.term
;
┌───────────────┬───────┐
│ term │ count │
├───────────────┼───────┤
│ subscription1 │ 3 │
│ subscription2 │ 3 │
│ subscription3 │ 1 │
│ subscription4 │ 3 │
└───────────────┴───────┘
(4 rows)
【讨论】:
太棒了!这似乎工作得很好。我很好奇您是否还知道如何修改它,文档中缺少的订阅 1/订阅 2 也适用于计数?换句话说,如果subscription1.subscribed = true
或subscription1
完全丢失,两者都应该添加到计数中吗?我正在尝试自己弄清楚,但是这种级别的 SQL 对我来说有点陌生。感谢您的回答!
我尝试了编辑,我发现了两个问题:首先,当表中有一条记录时检查 null 有效,如果添加第二条记录(或第三条等),则 null计数永远不会增加。例如,如果您复制 values
和 id=2
并运行查询,subscription4 仍将是 1(它应该是 2)。其次,如果 subscriptions
节点完全丢失(因此 jsonb 的示例值将只是
,这不会被视为真实/计数。我正在尝试自己修复它,但是如果你想帮助我也很感激!
@Josh 那个左连接代码有问题——很抱歉。我将其替换为可以处理缺少键条件的简化查询。以上是关于有没有办法对 jsonb 对象中的值列表应用条件?的主要内容,如果未能解决你的问题,请参考以下文章
如何为 postgresql 中的唯一(不包括顺序)JSONB 列创建约束