【发布时间】:2022-01-06 16:00:55
【问题描述】:
我们在我们的一个数据库表中使用 Postgres jsonb 类型。表结构如下:
CREATE TABLE IF NOT EXISTS public.draft_document (
id bigserial NOT NULL PRIMARY KEY,
...
document jsonb NOT NULL,
ein_search character varying(11) NOT NULL
);
CREATE INDEX IF NOT EXISTS count_draft_document_idx ON public.draft_document USING btree (ein_search);
CREATE INDEX IF NOT EXISTS read_draft_document_idx ON public.draft_document USING btree (id, ein_search);
document 列的 json 结构可能会有所不同。以下是document 的可能架构示例:
"withholdingCredit": {
"type": "array",
"items": {
"$ref": "#/definitions/withholding"
}
}
withholding 结构(数组元素)在哪里:
"withholding": {
"properties": {
...
"proportionalityIndicator": {
"type": "boolean"
},
"tribute": {
"$ref": "#/definitions/tribute"
},
"payingSourceEin": {
"type": "string"
},
"value": {
"type": "number"
}
...
}
...
},
"tribute": {
"type": "object",
"properties": {
"code": {
"type": "number"
},
"additionalCode": {
"type": "number"
}
...
}
}
这里是一个将json转换成documentjsonb列的例子:
{
"withholdingCredit":[
{
"value": 15000,
"tribute":{
"code": 1216,
"additionalCode": 2
},
"payingSourceEin": "03985506123132",
"proportionalityIndicator": false
},
...
{
"value": 98150,
"tribute":{
"code": 3155,
"additionalCode": 1
},
"payingSourceEin": "04185506123163",
"proportionalityIndicator": false
}
]
}
数组中元素的最大数量可以变化,最大限制为 100.000(十万)个元素。这是业务限制。
我们需要一个分页选择查询,它返回分解后的 withholding 数组(每行 1 个元素),其中每一行还包含 withholding 元素 value 和 array length 中的 sum。
查询还需要通过proportionalityIndicator、tribute-->code、tribute-->additionalCode、payingSourceEin返回预扣税ordered。比如:
| id | sum | jsonb_array_length | jsonb_array_elements |
|---|---|---|---|
| 30900 | 1.800.027 | 2300 | {"value":15000,"tribute":{"code":1216,...}, ...} |
| ... | ... | ... | { ... } |
| 30900 | 1.800.027 | 2300 | {"value":98150,"tribute":{"code":3155,...}, ...} |
我们定义了以下查询:
SELECT dft.id,
SUM((elem->>'value')::NUMERIC),
jsonb_array_length(dft.document->'withholdingCredit'),
jsonb_array_elements(jsonb_agg(elem
ORDER BY
elem->>'proportionalityIndicator',
(elem->'tribute'->>'code')::NUMERIC,
(elem->'tribute'->>'additionalCode')::NUMERIC,
elem->>'payingSourceEin'))
FROM
draft_document dft
CROSS JOIN LATERAL jsonb_array_elements(dft.document->'withholdingCredit') arr(elem)
WHERE (dft.document->'withholdingCredit') IS NOT NULL
AND dft.id = :id
AND dft.ein_search = :ein_search
GROUP BY dft.id
LIMIT :limit OFFSET :offset;
此查询有效,但当我们在 jsonb 数组中有大量元素时,性能会受到限制。 欢迎任何关于如何改进它的建议。
顺便说一句,我们使用的是 Postgres 9.6。
【问题讨论】:
-
放弃使用 JSON 的反规范化,创建一个正确规范化的模型。
-
9.6 版刚刚收到最后一个补丁,不再支持。计划升级到更新的版本,例如 13 或 14。从 json 对象中提取 100.000 个元素永远不会很快。
-
如果您需要以单独的方式管理 JSON 的各个部分以用于分页或其他目的,那么我将规范化数据库模型并将数据分离到不同的相关实体中。分页和管理将变得非常简单。当您需要将文档作为一个整体来处理并且您对分析它或查看它的细节没有兴趣时,JSON 非常有用。
-
感谢您的反馈。我们计划对该模型的一部分进行规范化。我们选择使用 json 作为
document,因为它的结构需要灵活。
标签: json postgresql performance indexing jsonb