如何合并多个 PostgreSQL JSON 数组,这些数组是另一个 JSON 数组更新其所有元素的结果?

How To Merge Multiple PostgreSQL JSON Arrays That Are The Result Of Another JSON Array Update On All Of Its Elements?

给定以下两 table 列 jsonb 类型:

dividend_actual

{
  "dividends": [
    {
      "amount": "2.9800",
      "balanceDate": "2020-06-30T00:00:00Z"
    },
    {
      "amount": "4.3100",
      "balanceDate": "2019-06-30T00:00:00Z"
    }
  ],
  "lastUpdated": "2020-11-16T14:50:51.289649512Z",
  "providerUpdateDate": "2020-11-16T00:00:00Z"
}

dividend_forecast

{
  "dividends": [
    {
      "amount": "2.3035",
      "balanceDate": "2021-06-01T00:00:00Z"
    },
    {
      "amount": "3.0452",
      "balanceDate": "2022-06-01T00:00:00Z"
    },
    {
      "amount": "3.1845",
      "balanceDate": "2023-06-01T00:00:00Z"
    }
  ],
  "lastForecasted": "2020-11-13T00:00:00Z",
  "providerUpdateDate": "2020-11-16T00:00:00Z"
}

我想合并来自 dividend_actualdividend_forecast 的两个 dividends 数组,但在合并它们之前我想在每个数组上添加一个额外的字段 (forecast)单个对象。

我确实尝试了以下方法:

SELECT
    dividends
FROM
    stock_financial AS f
    INNER JOIN instrument AS i ON i.id = f.instrument_id,
    jsonb_array_elements(
        (f.dividend_forecast->'dividends' || jsonb '{"forecast": true}') || 
        (f.dividend_actual->'dividends' || jsonb '{"forecast": false}')
    ) AS dividends
WHERE
    i.symbol = 'ASX_CBA'
ORDER BY
    dividends ->>'balanceDate' DESC;

以上查询给出了以下结果:

{"forecast":true}
{"forecast":false}
{"amount":"3.1845","balanceDate":"2023-06-01T00:00:00Z"}
{"amount":"3.0452","balanceDate":"2022-06-01T00:00:00Z"}
{"amount":"2.3035","balanceDate":"2021-06-01T00:00:00Z"}
{"amount":"2.9800","balanceDate":"2020-06-30T00:00:00Z"}
{"amount":"4.3100","balanceDate":"2019-06-30T00:00:00Z"}

但我需要的是以下输出:

{"amount":"3.1845","balanceDate":"2023-06-01T00:00:00Z","forecast":true}
{"amount":"3.0452","balanceDate":"2022-06-01T00:00:00Z","forecast":true}
{"amount":"2.3035","balanceDate":"2021-06-01T00:00:00Z","forecast":true}
{"amount":"2.9800","balanceDate":"2020-06-30T00:00:00Z","forecast":false}
{"amount":"4.3100","balanceDate":"2019-06-30T00:00:00Z","forecast":false}

事实证明,默认情况下无法在单个操作中更新 json 数组中的多个 jsons 对象。

要做到这一点,需要创建 Postgres function

-- the params are the same as in aforementioned `jsonb_set`
CREATE OR REPLACE FUNCTION update_json_array_elements(target jsonb, path text[], new_value jsonb)
  RETURNS jsonb language sql AS $$
    -- aggregate the jsonb from parts created in LATERAL
    SELECT jsonb_agg(updated_jsonb)
    -- split the target array to individual objects...
    FROM jsonb_array_elements(target) individual_object,
    -- operate on each object and apply jsonb_set to it. The results are aggregated in SELECT
    LATERAL jsonb_set(individual_object, path, new_value) updated_jsonb
  $$;

以上功能由kubak in this answer:

建议

结合这个查询:

SELECT
    dividends
FROM
    stock_financial AS f
    INNER JOIN instrument AS i ON i.id = f.instrument_id,
    jsonb_array_elements(
        update_json_array_elements(f.dividend_forecast->'dividends', '{forecast}', 'true') ||
        update_json_array_elements(f.dividend_actual->'dividends', '{forecast}', 'false')
    ) AS dividends
WHERE
    i.symbol = 'ASX_CBA'
ORDER BY
    dividends ->>'balanceDate' DESC;

然后我得到以下输出,这正是我需要的:

{"amount":"3.1845","forecast":true,"balanceDate":"2023-06-01T00:00:00Z"}
{"amount":"3.0452","forecast":true,"balanceDate":"2022-06-01T00:00:00Z"}
{"amount":"2.3035","forecast":true,"balanceDate":"2021-06-01T00:00:00Z"}
{"amount":"2.9800","forecast":false,"balanceDate":"2020-06-30T00:00:00Z"}
{"amount":"4.3100","forecast":false,"balanceDate":"2019-06-30T00:00:00Z"}