SUM & GROUP BY on an array of composite type

为君一笑 提交于 2019-12-11 10:59:25

问题


I have a column with an array of composite type (text, decimal, timestamp) as data type. I want to create a query to sum the total of the double column of the composite type. Also I want to perform a group by on the date(day-month-year) of the date time.

Can anyone show me an example of explain how this an be done?

Definition of table and type:

create type stage as (
   Stage_Name        text,
   Stage_Distance    decimal,
   Stage_Start_Time  timestamp
);

CREATE TABLE "Event" (
  "Id" serial NOT NULL,
  "Location" text,
  "Date_Range" daterange,
  "Surface" text,
  "Stage_Information" stage[],
  CONSTRAINT "PK_Event" PRIMARY KEY ("Id")
);

Example data

{"(Newtownards,1.5,\"2015-04-03 18:28:00\")"
,"(\"Bulls Brook\",13.4,\"2015-04-04 09:04:00\")"}

Expected results:

Sum(1.5 + 13.4) = 14.9

Group by 2015-04-03, 2015-04-04


回答1:


Assuming current Postgres version 9.4 for lack of information.

Proper design

First of all, consider database normalization. An additional table instead of the column "Stage_Information" is typically the superior solution:

CREATE TABLE stage (
  stage_id  serial PRIMARY KEY
, event_id  int NOT NULL REFERENCES event
, name      text        -- possibly NOT NULL
, distance  numeric     -- possibly NOT NULL
, starttime timestamp   -- possibly NOT NULL
);

It doesn't occupy much more disk space either, array overhead is similar to table overhead. Only the additional index needs some more space. But many queries on the base table will be faster, and updates will be much cheaper, and everything will be cleaner and simpler.

Don't mix quoted and unquoted capitalization with your identifiers. That is very error prone. Use unquoted, legal, lower-case names exclusively if you can.

  • Are PostgreSQL column names case-sensitive?

The query would then be:

SELECT e.id, s.starttime::date AS day
     , sum(s.distance) AS sum_distance
FROM   "Event" e
LEFT   JOIN stage s ON s.event_id = e.id
WHERE  e.id = 1
GROUP  BY 1, 2;

Solution for the problem at hand

While stuck with your current design, you need to unnest() the array to apply aggregate functions to its elements. An then you need to decompose the composite values. Use a LATERAL join:

SELECT e.id, (s.st).stage_start_time::date AS day
     , sum((s.st).stage_distance) AS sum_distance
FROM   "Event" e
LEFT   JOIN LATERAL unnest(e."Stage_Information") s(st) ON true
WHERE  e.id = 1
GROUP  BY 1, 2;

Note the parentheses around (s.st) (the column alias for the unnested column). You need those to access elements of a composite type (row type).

Why LEFT JOIN LATERAL ... ON true?

  • Call a set-returning function with an array argument multiple times


来源:https://stackoverflow.com/questions/29310815/sum-group-by-on-an-array-of-composite-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!