2016-08-20 395 views
1

我有一个表(表A),其中包含一个包含JSON编码数据的文本列。PosgreSQL:高效地将JSON数组拆分成行

JSON数据始终是一个数组,其中包含一个和几千个普通对象。

我有另一个表(表B)与几个列,其中包括与“JSON”

我想选择所有从表A中的行的数据类型的列,JSON数组分成它的元素和将每个元素插入表B

奖励目标:每个对象(几乎)总是有一个键,x。我想将x的值拉出列,并从原始对象(如果存在)中删除x

如:表A

| id | json_array (text)    | 
+----+--------------------------------+ 
| 1 | '[{"x": 1}, {"y": 8}]'   | 
| 2 | '[{"x": 2, "y": 3}, {"x": 1}]' | 
| 3 | '[{"x": 8, "z": 2}, {"z": 3}]' | 
| 4 | '[{"x": 5, "y": 2, "z": 3}]' | 

...会变成:表B

| id | a_id | x | json (json)  | 
+----+------+------+--------------------+ 
| 0 | 1 | 1 | '{}'    | 
| 1 | 1 | NULL | '{"y": 8}'   | 
| 2 | 2 | 2 | '{"y": 3}'   | 
| 3 | 2 | 1 | '{}'    | 
| 4 | 3 | 8 | '{"y": 2}'   | 
| 5 | 3 | NULL | '{"z": 3}'   | 
| 6 | 4 | 5 | '{"y": 2, "z": 3}' | 

这最初具有对数百万行的工作,并随后将需要在常规运行间隔,所以使其高效率将是一个优先事项。

不使用循环和PL/PgSQL可以做到这一点吗?我没有取得太多进展。

+0

你可以使用'jsonb'(即PG9.5)吗? 'json'数据类型不适合操作对象,'jsonb'有更多的选项。 'jsonb'很容易删除'x',更复杂的是'json'。 – Patrick

回答

2

json数据类型不适合(或打算)在数据库级别进行修改。从JSON对象中提取"x"对象因此很麻烦,尽管它可以完成。

您应该创建表B(有希望更有创意的列名比"json";我使用item这里),使idserial开始于0。json解决方案则是这样的:

INSERT INTO b (a_id, x, item) 
    SELECT sub.a_id, sub.x, 
     ('{' || 
     string_agg(
      CASE WHEN i.k IS NULL THEN '' ELSE '"' || i.k || '":' || i.v END, 
      ', ') || 
     '}')::json 
    FROM (
    SELECT a.id AS a_id, (j.items->>'x')::integer AS x, j.items 
    FROM a, json_array_elements(json_array) j(items)) sub 
    LEFT JOIN json_each(sub.items) i(k,v) ON i.k <> 'x' 
    GROUP BY sub.a_id, sub.x 
    ORDER BY sub.a_id; 

在子查询这个提取a_idx值,以及JSON对象。在外部查询中,JSON对象被分解为其各个部分以及丢弃键x的对象(LEFT JOIN ON i.k <> 'x')。在选择列表中,片段再次与字符串连接一起放回到一起并分组为复合对象。

这一定是这样的,因为json没有任何后果的内置操作函数。这适用于PG版本9.3+,即自从关于JSON支持问题以来的自古以来。

如果您正在使用PG9.5 +,解决的办法是通过铸造更加简单jsonb

INSERT INTO b (a_id, x, item) 
    SELECT a.id, (j.items->>'x')::integer, j.items #- '{x}' 
    FROM a, jsonb_array_elements(json_array::jsonb) j(items); 

#-运营商对jsonb数据类型在这里所做的一切肮脏的工作。显然,幕后有很多工作要做,将json转换为jsonb,所以如果您发现需要更频繁地操作JSON对象,那么最好使用jsonb类型开始。在你的情况下,我建议你用EXPLAIN ANALYZE SELECT ...做一些基准测试(你可以放心地忘掉INSERT),然后再测试10,000行,看看哪个最适合你的设置。

+0

我们现在不在9.5,但不幸的是,希望它不会太长。非常感谢全面的答复。 – aidan