【问题标题】:Expand a postgres array of JSON within a single column into multiple records将单个列中 JSON 的 postgres 数组扩展为多条记录
【发布时间】:2019-09-16 05:51:11
【问题描述】:

我有一个名为goals 的表,其中每一行包含一个目标。有关目标的特定指标的数据存储在名为goal_metrics 的 JSON 数组中。有时有 1 个指标,有时有 0 个指标,有时有多个指标。我想将所有目标指标提取到一个新表中,每个指标有 1 行以及该指标所属的原始goal_id。

这是table1 目前的样子:

+---------+------------+----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| goal_id | goal_owner | goal_create_at |                                                                                                                                                                                                             goal_metrics                                                                                                                                                                                                             |
+---------+------------+----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|   69660 |     148191 |     1566576355 | [{'metric_owner': '148191', 'metric_create_at': '1566576374', 'metric_target': '10'}, {'metric_owner': '148191', 'metric_create_at': '1566576403', 'metric_target': '1'}, {'metric_owner': '148191', 'metric_create_at': '1566576428', 'metric_target': '3'}, {'metric_owner': '148191', 'metric_create_at': '1566576450', 'metric_target': '3'}]                                                                                    |
|   68443 |     146270 |     1565911160 | [{'metric_owner': '144534', 'metric_create_at': '1565911175', 'metric_target': '1'}, {'metric_owner': '144352', 'metric_create_at': '1565911191', 'metric_target': '1'}, {'metric_owner': '144352', 'metric_create_at': '1565911212', 'metric_target': '15'}, {'metric_owner': '146270', 'metric_create_at': '1565911353', 'metric_target': '23'}]                                                                                   |
|   68440 |     146270 |     1565910356 | [{'metric_owner': '144061', 'metric_create_at': '1565910380', 'metric_target': '3'}, {'metric_owner': '144061', 'metric_create_at': '1565910462', 'metric_target': '0'}, {'metric_owner': '144534', 'metric_create_at': '1565910523', 'metric_target': '1'}, {'metric_owner': '143866', 'metric_create_at': '1565911422', 'metric_target': '6'}]                                                                                     |
|   68442 |     146270 |     1565910746 | [{'metric_owner': '143866', 'metric_create_at': '1565910863', 'metric_target': '9'}, {'metric_owner': '143866', 'metric_create_at': '1565910881', 'metric_target': '1'}, {'metric_owner': '144534', 'metric_create_at': '1565910905', 'metric_target': '1'}, {'metric_owner': '146270', 'metric_create_at': '1565910927', 'metric_target': '1'}, {'metric_owner': '144534', 'metric_create_at': '1565910956', 'metric_target': '1'}] |
+---------+------------+----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

这就是我想要达到的目的:

+--------------+------------------+---------------+---------+
| metric_owner | metric_create_at | metric_target | goal_id |
+--------------+------------------+---------------+---------+
|       143321 |       1557774894 |            56 |   53513 |
|       143321 |       1557774933 |           100 |   53513 |
|       143321 |       1557774983 |            10 |   53513 |
|       143321 |       1557775102 |           100 |   53513 |
|       143321 |       1557775388 |           100 |   53513 |
|       148191 |       1566576374 |            10 |   69660 |
|       148191 |       1566576403 |             1 |   69660 |
|       148191 |       1566576428 |             3 |   69660 |
|       148191 |       1566576450 |             3 |   69660 |
|       144534 |       1565911175 |             1 |   68443 |
|       144352 |       1565911191 |             1 |   68443 |
|       144352 |       1565911212 |            15 |   68443 |
|       146270 |       1565911353 |            23 |   68443 |
|       144061 |       1565910380 |             3 |   68440 |
|       144061 |       1565910462 |             0 |   68440 |
|       144534 |       1565910523 |             1 |   68440 |
|       143866 |       1565911422 |             6 |   68440 |
|       143866 |       1565910863 |             9 |   68442 |
|       143866 |       1565910881 |             1 |   68442 |
|       144534 |       1565910905 |             1 |   68442 |
|       146270 |       1565910927 |             1 |   68442 |
|       144534 |       1565910956 |             1 |   68442 |
+--------------+------------------+---------------+---------+

如果它只是一个常规的 JSON 列,我可以使用 SELECT goal_metric -> 'metrics_owner' AS metrics_owner,但因为它是一个可变长度的数组,所以这不起作用。我认为我需要使用JSON_TO_RECORDSET,并且我一直在尝试使用它,但我无法实现我想要的。

【问题讨论】:

  • 你想要jsonb_array_elements

标签: sql arrays json postgresql


【解决方案1】:

有不同的可能方式(Postgres JSON documentation):

demo:db<>fiddle


答:使用json_array_elements()

SELECT
    goal_id,
    (elems ->> 'metric_owner')::int AS metric_owner,
    (elems ->> 'metric_create_at')::int AS metric_create_at,
    (elems ->> 'metric_target')::int AS metric_target
FROM
    table1,
    json_array_elements(goal_metrics) as elems

这会将每个数组元素展开为一行。可以查询这些扩展的 JSON 对象的属性。这给出了text 类型。如果你想让它们成为int 类型,你需要一个演员表。


B:使用json_to_recordset()

SELECT
    goal_id,
    elems.*
FROM
    table1,
    json_to_recordset(goal_metrics) 
        AS elems(metric_owner int, metric_create_at int,metric_target int)

这一步与 (A) 中的相同。您只需要定义输出类型。


C:如果您必须定期执行此操作,那么定义一次输出类型可能会很有用。然后就可以在json_populate_recordset()内使用了:

CREATE TYPE metrics_type 
    AS (metric_owner int, metric_create_at int,metric_target int);

SELECT
    goal_id,
    elems.*
FROM
    table1,
    json_populate_recordset(null::metrics_type, goal_metrics) AS elems

【讨论】:

  • 实际上,如果@CurtLH 需要定期执行此操作,那么适当规范化的数据模型是更好的选择。
  • @a_horse_with_no_name 是的,这当然是最可取的方式。但是可能因为奇怪的接口之类的,他无法更改数据模型……
  • 谢谢@S-Man。这正是我所需要的!
【解决方案2】:
first un nest and give a try with ->> option

 with basejson  as (select goal_id ,goal_owner,unnest(goal_metrics) goal_metrics  from jsonmetrics ) select goal_id, goal_owner,goal_metrics->>'metric_owner' "MstricOwner",goal_metrics->>'metric_target' "MetricTGT"  from basejson;
 goal_id | goal_owner | MstricOwner | MetricTGT 
---------+------------+-------------+-----------
       4 |          1 | 143866      | 9
       2 |          2 | 143866      | 9
       2 |          2 | 143867      | 9


【讨论】:

    猜你喜欢
    • 2020-03-03
    • 1970-01-01
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多