将包含 JSONB 字符串数组的行拆分为两个不同的行 (PostgreSQL)
Splitting a row containing JSONB string array into two different rows (PostgreSQL)
给定一行如下所示(PostgreSQL 10 和 11):
CREATE TABLE examples (
"id" varchar NOT NULL,
"type" varchar NOT NULL,
"relation_id" varchar NOT NULL,
"things" jsonb,
PRIMARY KEY ("id")
);
INSERT INTO examples(id, type, relation_id, things) values
('7287b283-f2d8-4940-94ae-c8253599d479', 'letter-number', 'relation-id-1', '["A", "B", "1", "2", "C"]');
INSERT INTO examples(id, type, relation_id, things) values
('7287b283-f2d8-4940-94ae-c8253599d480', 'letter-number', 'relation-id-2', '["A", "2", "C"]');
INSERT INTO examples(id, type, relation_id, things) values
('7287b283-f2d8-4940-94ae-c8253599d481', 'letter-number', 'relation-id-3', '[]');
您将如何继续将这些行拆分为:
'7287b283-f2d8-4940-94ae-c8253599d480', 'relation-id-1', 'number', '["2"]'
'7287b283-f2d8-4940-94ae-c8253599d482', 'relation-id-1', 'letter', '["A", "C"]'
.
本质上是拆分"type"字段,有条件地划分jsonb数组
假设:
- JSONB 中的值始终存在(它们可以是空数组)但它们的结构始终如此。
- 同一个table中还有其他
types
。
- 保持 relation_id 至关重要。
- JSONB 数组中的值是已知的,它们只有几个(比如说五个或六个)所以我们可以在查询中对它们进行硬编码。
- 更改必须持久化。原始行可以是removed/updated.
https://www.db-fiddle.com/f/4VV1tZD3pBYMiCmtTFtQnj/5 <-DB-fiddle.
我试过用子查询搞乱 json_array_elements
和 INSERT ... SELECT
,但现在我无处可去。
SELECT
id,
relation_id,
CASE WHEN elems ~ '[0-9]' THEN 'number' ELSE 'letter' END AS type, -- 2
jsonb_agg(elems) -- 3
FROM
examples,
jsonb_array_elements_text(things) elems -- 1
GROUP BY
1,2,3 -- 3
- 将数组扩展为每个元素一行
- 使用正则表达式检查元素是否为数字。如果是,创建新的类型编号,否则创建字母
- 按此新类型分组并重新聚合元素
有扩展名:
- 使用拆分行更新 table(删除旧的,插入新的)
- 空数组生成两种类型的行
WITH del AS (
DELETE FROM examples
RETURNING id, relation_id, type, things
)
INSERT INTO examples
SELECT
id || '_' || type,
relation_id,
type,
COALESCE(jsonb_agg(elems) FILTER (WHERE elems IS NOT NULL), '[]')
FROM (
SELECT
id,
relation_id,
CASE WHEN elems ~ '[0-9]' THEN 'number' ELSE 'letter' END AS type,
elems
FROM
del,
jsonb_array_elements_text(things) elems
UNION ALL
SELECT
id,
relation_id,
t,
null
FROM
examples,
unnest(array['number', 'letter']) as t
WHERE things = '[]'
) s
GROUP BY 1,2,3;
给定一行如下所示(PostgreSQL 10 和 11):
CREATE TABLE examples (
"id" varchar NOT NULL,
"type" varchar NOT NULL,
"relation_id" varchar NOT NULL,
"things" jsonb,
PRIMARY KEY ("id")
);
INSERT INTO examples(id, type, relation_id, things) values
('7287b283-f2d8-4940-94ae-c8253599d479', 'letter-number', 'relation-id-1', '["A", "B", "1", "2", "C"]');
INSERT INTO examples(id, type, relation_id, things) values
('7287b283-f2d8-4940-94ae-c8253599d480', 'letter-number', 'relation-id-2', '["A", "2", "C"]');
INSERT INTO examples(id, type, relation_id, things) values
('7287b283-f2d8-4940-94ae-c8253599d481', 'letter-number', 'relation-id-3', '[]');
您将如何继续将这些行拆分为:
'7287b283-f2d8-4940-94ae-c8253599d480', 'relation-id-1', 'number', '["2"]'
'7287b283-f2d8-4940-94ae-c8253599d482', 'relation-id-1', 'letter', '["A", "C"]'
.
本质上是拆分"type"字段,有条件地划分jsonb数组
假设:
- JSONB 中的值始终存在(它们可以是空数组)但它们的结构始终如此。
- 同一个table中还有其他
types
。 - 保持 relation_id 至关重要。
- JSONB 数组中的值是已知的,它们只有几个(比如说五个或六个)所以我们可以在查询中对它们进行硬编码。
- 更改必须持久化。原始行可以是removed/updated.
https://www.db-fiddle.com/f/4VV1tZD3pBYMiCmtTFtQnj/5 <-DB-fiddle.
我试过用子查询搞乱 json_array_elements
和 INSERT ... SELECT
,但现在我无处可去。
SELECT
id,
relation_id,
CASE WHEN elems ~ '[0-9]' THEN 'number' ELSE 'letter' END AS type, -- 2
jsonb_agg(elems) -- 3
FROM
examples,
jsonb_array_elements_text(things) elems -- 1
GROUP BY
1,2,3 -- 3
- 将数组扩展为每个元素一行
- 使用正则表达式检查元素是否为数字。如果是,创建新的类型编号,否则创建字母
- 按此新类型分组并重新聚合元素
有扩展名:
- 使用拆分行更新 table(删除旧的,插入新的)
- 空数组生成两种类型的行
WITH del AS (
DELETE FROM examples
RETURNING id, relation_id, type, things
)
INSERT INTO examples
SELECT
id || '_' || type,
relation_id,
type,
COALESCE(jsonb_agg(elems) FILTER (WHERE elems IS NOT NULL), '[]')
FROM (
SELECT
id,
relation_id,
CASE WHEN elems ~ '[0-9]' THEN 'number' ELSE 'letter' END AS type,
elems
FROM
del,
jsonb_array_elements_text(things) elems
UNION ALL
SELECT
id,
relation_id,
t,
null
FROM
examples,
unnest(array['number', 'letter']) as t
WHERE things = '[]'
) s
GROUP BY 1,2,3;