透视和聚合 1..n 重复记录 Postgres psql
Pivot and aggregate 1..n duplicate records Postgres psql
我有一个 table tbl_action
这样的:
game_id
action
action_datetime
1
start
2022-04-05T10:30+00
1
attack
2022-04-05T10:45+00
1
defend
2022-04-05T11:30+00
1
attack
2022-04-05T11:45+00
1
defend
2022-04-05T12:00+00
1
stop
2022-04-05T12:10+00
create table if not exists tblaction;
insert into "tblaction" (game_id, action_name, action_time) values (1,'start','2022-04-05T10:30+00'),
(2,'attack','2022-04-05T10:45+00'),
(3,'defend','2022-04-05T11:30+00'),
(4,'attack','2022-04-05T11:45+00'),
(5,'defend','2022-04-05T12:00+00'),
(6,'stop','2022-04-05T12:10+00');
我想像这样转动它:
game_id
start
attack1
defend1
...
attackn
defendn
stop
1
2022-04-05T10:30+00
2022-04-05T10:45+00
2022-04-05T11:30+00
...
2022-04-05T11:45+00
2022-04-05T12:00+00
2022-04-05T12:10+00
我的问题是如何聚合操作 1..n,而不是将所有每个唯一 action_name 聚合成一个数字。我正在使用 Postgres。
此 MySQL 示例代码在每个 action_name 出现 <2 次时有效,但我想要 1..n 有效的代码。我不想删除重复项。我不限MySQL我可以用最新版的Postgres
SELECT
game_id,
MAX( CASE WHEN action_name = 'start' THEN action_time ELSE NULL END ) AS "start",
MIN( CASE WHEN action_name = 'attack' THEN action_time ELSE NULL END ) AS "attack",
MIN( CASE WHEN action_name = 'defend' THEN action_time ELSE NULL END ) AS "defend",
MAX( CASE WHEN action_name = 'attack' THEN action_time ELSE NULL END ) AS "attack",
MAX( CASE WHEN action_name = 'defend' THEN action_time ELSE NULL END ) AS "defend",
MAX( CASE WHEN action_name = 'stop' THEN action_time ELSE NULL END ) AS "stop"
FROM
tblaction
GROUP BY
game_id
ORDER BY
game_id ASC;
我阅读了 Postgres tablefunc 文档并在 pgadmin4 中尝试了 \crosstabview 并得到了错误 \crosstabview: query result contains multiple data values for row "1", column "attack"
SELECT game_id, action_name, action_time FROM tblaction \crosstabview
你可以尝试使用带有ROW_NUMER
window 函数的子查询,它可以帮助你通过 game_id
, action_name
列生成行号,然后你可以使用条件聚合函数为此 rn
SELECT
game_id,
MAX(CASE WHEN action_name = 'start' THEN action_time END ) AS "start",
MAX(CASE WHEN action_name = 'attack' AND rn = 1 THEN action_time END ) AS "attack1",
MAX(CASE WHEN action_name = 'defend' AND rn = 1 THEN action_time END ) AS "defend1",
MAX(CASE WHEN action_name = 'attack' AND rn = 2 THEN action_time END ) AS "attack",
MAX(CASE WHEN action_name = 'defend' AND rn = 2 THEN action_time END ) AS "defend",
MAX(CASE WHEN action_name = 'stop' THEN action_time END ) AS "stop"
FROM
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY game_id,action_name ORDER BY action_time) rn
FROM tblaction
) t1
GROUP BY
game_id
ORDER BY
game_id ASC;
我建议用GROUP_CONCAT会更简单灵活。
注意 该问题据说适用于 mySQL 或 Postgres。请参阅 https://dbfiddle.uk/?rdbms=postgres_10&fiddle=8719aedb7ab5736caf79fe86e76d6f40 以了解使用 STRING_AGG( ~ ,',')
而不是 GROUP_CONCAT
的 Postgres 版本,否则相同
create table tblaction (
game_id int,
action_name varchar(20),
action_time varchar(25)
);
insert into tblaction
(game_id, action_name, action_time) values
(1,'start','2022-04-05T10:30+00'),
(1,'attack','2022-04-05T10:45+00'),
(1,'defend','2022-04-05T11:30+00'),
(1,'attack','2022-04-05T11:45+00'),
(1,'defend','2022-04-05T12:00+00'),
(1,'stop','2022-04-05T12:10+00');
✓
✓
SELECT
game_id,
GROUP_CONCAT(case when action_name='start' then action_time end) start,
GROUP_CONCAT(case when action_name='stop' then action_time end) stop,
COUNT(action_time) "number",
GROUP_CONCAT(case when action_name='attack' then action_time end) "attacks",
GROUP_CONCAT(case when action_name='defend' then action_time end) "defends"
FROM tblaction
GROUP BY
game_id;
game_id | start | stop | number | attacks | defends
------: | :------------------ | :------------------ | -----: | :-------------------------------------- | :--------------------------------------
1 | 2022-04-05T10:30+00 | 2022-04-05T12:10+00 | 6 | 2022-04-05T10:45+00,2022-04-05T11:45+00 | 2022-04-05T11:30+00,2022-04-05T12:00+00
db<>fiddle here
我有一个 table tbl_action
这样的:
game_id | action | action_datetime |
---|---|---|
1 | start | 2022-04-05T10:30+00 |
1 | attack | 2022-04-05T10:45+00 |
1 | defend | 2022-04-05T11:30+00 |
1 | attack | 2022-04-05T11:45+00 |
1 | defend | 2022-04-05T12:00+00 |
1 | stop | 2022-04-05T12:10+00 |
create table if not exists tblaction;
insert into "tblaction" (game_id, action_name, action_time) values (1,'start','2022-04-05T10:30+00'),
(2,'attack','2022-04-05T10:45+00'),
(3,'defend','2022-04-05T11:30+00'),
(4,'attack','2022-04-05T11:45+00'),
(5,'defend','2022-04-05T12:00+00'),
(6,'stop','2022-04-05T12:10+00');
我想像这样转动它:
game_id | start | attack1 | defend1 | ... | attackn | defendn | stop |
---|---|---|---|---|---|---|---|
1 | 2022-04-05T10:30+00 | 2022-04-05T10:45+00 | 2022-04-05T11:30+00 | ... | 2022-04-05T11:45+00 | 2022-04-05T12:00+00 | 2022-04-05T12:10+00 |
我的问题是如何聚合操作 1..n,而不是将所有每个唯一 action_name 聚合成一个数字。我正在使用 Postgres。
此 MySQL 示例代码在每个 action_name 出现 <2 次时有效,但我想要 1..n 有效的代码。我不想删除重复项。我不限MySQL我可以用最新版的Postgres
SELECT
game_id,
MAX( CASE WHEN action_name = 'start' THEN action_time ELSE NULL END ) AS "start",
MIN( CASE WHEN action_name = 'attack' THEN action_time ELSE NULL END ) AS "attack",
MIN( CASE WHEN action_name = 'defend' THEN action_time ELSE NULL END ) AS "defend",
MAX( CASE WHEN action_name = 'attack' THEN action_time ELSE NULL END ) AS "attack",
MAX( CASE WHEN action_name = 'defend' THEN action_time ELSE NULL END ) AS "defend",
MAX( CASE WHEN action_name = 'stop' THEN action_time ELSE NULL END ) AS "stop"
FROM
tblaction
GROUP BY
game_id
ORDER BY
game_id ASC;
我阅读了 Postgres tablefunc 文档并在 pgadmin4 中尝试了 \crosstabview 并得到了错误 \crosstabview: query result contains multiple data values for row "1", column "attack"
SELECT game_id, action_name, action_time FROM tblaction \crosstabview
你可以尝试使用带有ROW_NUMER
window 函数的子查询,它可以帮助你通过 game_id
, action_name
列生成行号,然后你可以使用条件聚合函数为此 rn
SELECT
game_id,
MAX(CASE WHEN action_name = 'start' THEN action_time END ) AS "start",
MAX(CASE WHEN action_name = 'attack' AND rn = 1 THEN action_time END ) AS "attack1",
MAX(CASE WHEN action_name = 'defend' AND rn = 1 THEN action_time END ) AS "defend1",
MAX(CASE WHEN action_name = 'attack' AND rn = 2 THEN action_time END ) AS "attack",
MAX(CASE WHEN action_name = 'defend' AND rn = 2 THEN action_time END ) AS "defend",
MAX(CASE WHEN action_name = 'stop' THEN action_time END ) AS "stop"
FROM
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY game_id,action_name ORDER BY action_time) rn
FROM tblaction
) t1
GROUP BY
game_id
ORDER BY
game_id ASC;
我建议用GROUP_CONCAT会更简单灵活。
注意 该问题据说适用于 mySQL 或 Postgres。请参阅 https://dbfiddle.uk/?rdbms=postgres_10&fiddle=8719aedb7ab5736caf79fe86e76d6f40 以了解使用 STRING_AGG( ~ ,',')
而不是 GROUP_CONCAT
的 Postgres 版本,否则相同
create table tblaction ( game_id int, action_name varchar(20), action_time varchar(25) ); insert into tblaction (game_id, action_name, action_time) values (1,'start','2022-04-05T10:30+00'), (1,'attack','2022-04-05T10:45+00'), (1,'defend','2022-04-05T11:30+00'), (1,'attack','2022-04-05T11:45+00'), (1,'defend','2022-04-05T12:00+00'), (1,'stop','2022-04-05T12:10+00');
✓ ✓
SELECT game_id, GROUP_CONCAT(case when action_name='start' then action_time end) start, GROUP_CONCAT(case when action_name='stop' then action_time end) stop, COUNT(action_time) "number", GROUP_CONCAT(case when action_name='attack' then action_time end) "attacks", GROUP_CONCAT(case when action_name='defend' then action_time end) "defends" FROM tblaction GROUP BY game_id;
game_id | start | stop | number | attacks | defends ------: | :------------------ | :------------------ | -----: | :-------------------------------------- | :-------------------------------------- 1 | 2022-04-05T10:30+00 | 2022-04-05T12:10+00 | 6 | 2022-04-05T10:45+00,2022-04-05T11:45+00 | 2022-04-05T11:30+00,2022-04-05T12:00+00
db<>fiddle here