带有COALESCE的Postgres非常困难的动态选择语句

时间:2018-06-26 19:59:28

标签: postgresql

具有这样的表和数据

CREATE TABLE solicitations
(
  id SERIAL PRIMARY KEY,
  name text
);

CREATE TABLE donations
(
  id SERIAL PRIMARY KEY,
  solicitation_id integer REFERENCES solicitations, -- can be null
  created_at timestamp without time zone NOT NULL DEFAULT (now() at time zone 'utc'),
  amount bigint NOT NULL DEFAULT 0
);


INSERT INTO solicitations (name) VALUES 
  ('solicitation1'), ('solicitation2');

INSERT INTO donations (created_at, solicitation_id, amount) VALUES 
  ('2018-06-26', null, 10), ('2018-06-26', 1, 20), ('2018-06-26', 2, 30),
  ('2018-06-27', null, 10), ('2018-06-27', 1, 20),
  ('2018-06-28', null, 10), ('2018-06-28', 1, 20), ('2018-06-28', 2, 30);

如何仅使用postgres在以下select语句中使请求ID动态化?

SELECT
"created_at"
-- make dynamic this begins
, COALESCE("no_solicitation", 0) AS "no_solicitation"
, COALESCE("1", 0) AS "1"
, COALESCE("2", 0) AS "2"
-- make dynamic this ends
FROM crosstab(
  $source_sql$
    SELECT
      created_at::date as row_id
    , COALESCE(solicitation_id::text, 'no_solicitation') as category
    , SUM(amount) as value
    FROM donations
    GROUP BY row_id, category
    ORDER BY row_id, category
  $source_sql$
, $category_sql$
  -- parametrize with ids from here begins
  SELECT unnest('{no_solicitation}'::text[] || ARRAY(SELECT DISTINCT id::text FROM solicitations ORDER BY id))
  -- parametrize with ids from here ends
  $category_sql$
) AS ct (
  "created_at" date
-- make dynamic this begins
, "no_solicitation" bigint
, "1" bigint
, "2" bigint
-- make dynamic this ends
)

select应该返回这样的数据

created_at  no_solicitation  1    2
____________________________________
2018-06-26  10               20   30
2018-06-27  10               20   0
2018-06-28  10               20   30

应参数化选择的征集ID与

中的相同
    SELECT unnest('{no_solicitation}'::text[] || ARRAY(SELECT DISTINCT id::text FROM solicitations ORDER BY id))

一个人可以摆弄代码here

1 个答案:

答案 0 :(得分:0)

我决定使用json,它比交叉表要简单得多

WITH
  all_solicitation_ids AS (
    SELECT
      unnest('{no_solicitation}'::text[] ||
      ARRAY(SELECT DISTINCT id::text FROM solicitations ORDER BY id))
    AS col
  )
, all_days AS (
  SELECT
    -- TODO: compute days ad hoc, from min created_at day of donations to max created_at day of donations
    generate_series('2018-06-26', '2018-06-28', '1 day'::interval)::date
  AS col
)
, all_days_and_all_solicitation_ids AS (
  SELECT
    all_days.col AS created_at
  , all_solicitation_ids.col AS solicitation_id
  FROM all_days, all_solicitation_ids
  ORDER BY all_days.col, all_solicitation_ids.col
)
, donations_ AS (
  SELECT
    created_at::date as created_at
  , COALESCE(solicitation_id::text, 'no_solicitation') as solicitation_id
  , SUM(amount) as amount
  FROM donations
  GROUP BY created_at, solicitation_id
  ORDER BY created_at, solicitation_id
)
, donations__ AS (
  SELECT
    all_days_and_all_solicitation_ids.created_at
  , all_days_and_all_solicitation_ids.solicitation_id
  , COALESCE(donations_.amount, 0) AS amount
  FROM all_days_and_all_solicitation_ids
  LEFT JOIN donations_
    ON all_days_and_all_solicitation_ids.created_at = donations_.created_at
   AND all_days_and_all_solicitation_ids.solicitation_id = donations_.solicitation_id
)

SELECT
  jsonb_object_agg(solicitation_id, amount) ||
  jsonb_object_agg('date', created_at)
  AS data
FROM donations__
GROUP BY created_at

结果

data
______________________________________________________________
{"1": 20, "2": 30, "date": "2018-06-28", "no_solicitation": 10}
{"1": 20, "2": 30, "date": "2018-06-26", "no_solicitation": 10}
{"1": 20, "2": 0, "date": "2018-06-27", "no_solicitation": 10}

认为与我要求的不一样。 为此,它仅返回data列,而不返回date, no_solicitation, 1, 2, ....列,我需要使用json_to_record,但是我不知道如何动态产生其as参数