问题
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