复合类型数组上的 SUM 和 GROUP BY
SUM & GROUP BY on an array of composite type
我有一个列,其数据类型为复合类型 (text, decimal, timestamp)
的数组。我想创建一个查询来对复合类型的双列的总和求和。我还想在日期时间的日期(日-月-年)上执行分组。
任何人都可以给我一个例子来解释这是如何完成的吗?
table 的定义和类型:
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")
);
示例数据
{"(Newtownards,1.5,\"2015-04-03 18:28:00\")"
,"(\"Bulls Brook\",13.4,\"2015-04-04 09:04:00\")"}
预期结果:
总和(1.5 + 13.4) = 14.9
按 2015-04-03、2015-04-04 分组
由于缺少信息,假定当前的 Postgres 版本为 9.4。
合理的设计
首先考虑database normalization。一个额外的 table 而不是列 "Stage_Information"
通常是更好的解决方案:
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
);
它也不占用更多的磁盘space,阵列开销与table开销相似。只有额外的索引需要更多 space。但是基于 table 的许多查询会更快,更新会 便宜很多,一切都会更干净、更简单。
不要将带引号和不带引号的大写字母与您的标识符混用。这是非常容易出错的。如果可以,只使用不带引号的合法小写名称。
- Are PostgreSQL column names case-sensitive?
查询将是:
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;
手头问题的解决方案
虽然坚持当前的设计,但您需要 unnest()
数组以将聚合函数应用于其元素。然后你需要分解复合值。使用 LATERAL
连接:
SELECT e.id, <b>(</b>s.st<b>)</b>.stage_start_time::date AS day
, sum(<b>(</b>s.st<b>)</b>.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;
请注意 (s.st)
两边的括号(未嵌套列的列别名)。您需要那些来访问 composite type (row type).
的元素
为什么 LEFT JOIN LATERAL ... ON true
?
- Call a set-returning function with an array argument multiple times
我有一个列,其数据类型为复合类型 (text, decimal, timestamp)
的数组。我想创建一个查询来对复合类型的双列的总和求和。我还想在日期时间的日期(日-月-年)上执行分组。
任何人都可以给我一个例子来解释这是如何完成的吗?
table 的定义和类型:
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")
);
示例数据
{"(Newtownards,1.5,\"2015-04-03 18:28:00\")"
,"(\"Bulls Brook\",13.4,\"2015-04-04 09:04:00\")"}
预期结果:
总和(1.5 + 13.4) = 14.9
按 2015-04-03、2015-04-04 分组
由于缺少信息,假定当前的 Postgres 版本为 9.4。
合理的设计
首先考虑database normalization。一个额外的 table 而不是列 "Stage_Information"
通常是更好的解决方案:
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
);
它也不占用更多的磁盘space,阵列开销与table开销相似。只有额外的索引需要更多 space。但是基于 table 的许多查询会更快,更新会 便宜很多,一切都会更干净、更简单。
不要将带引号和不带引号的大写字母与您的标识符混用。这是非常容易出错的。如果可以,只使用不带引号的合法小写名称。
- Are PostgreSQL column names case-sensitive?
查询将是:
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;
手头问题的解决方案
虽然坚持当前的设计,但您需要 unnest()
数组以将聚合函数应用于其元素。然后你需要分解复合值。使用 LATERAL
连接:
SELECT e.id, <b>(</b>s.st<b>)</b>.stage_start_time::date AS day
, sum(<b>(</b>s.st<b>)</b>.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;
请注意 (s.st)
两边的括号(未嵌套列的列别名)。您需要那些来访问 composite type (row type).
为什么 LEFT JOIN LATERAL ... ON true
?
- Call a set-returning function with an array argument multiple times