TL;DR:我无法编写一个在其递归部分中不使用聚合函数的递归Postgres查询。是否有其他方法来编写下面显示的递归查询?
如果我将
我考虑尝试使用嵌套查询将递归展开到两个或三个级别,因为这对我的用例来说没问题,但我想在尝试之前先在这里问一下。
另一个选择可能是更改模式,使得运动项目不能属于多个类别。我不确定在那种情况下查询会是什么样子,但可能会更简单?
非常感谢您的帮助。
假设我们有一些体育运动:
CREATE TABLE sports (id INTEGER, name TEXT);
INSERT INTO sports VALUES (1, '100 meter sprint');
INSERT INTO sports VALUES (2, '400 meter sprint');
INSERT INTO sports VALUES (3, '50 meter swim');
INSERT INTO sports VALUES (4, '100 meter swim');
以下是参加这些运动的运动员所用的一些圈数:
CREATE TABLE lap_times (sport_id INTEGER, athlete TEXT, seconds NUMERIC);
INSERT INTO lap_times VALUES (1, 'Alice', 10);
INSERT INTO lap_times VALUES (1, 'Bob', 11);
INSERT INTO lap_times VALUES (1, 'Claire', 12);
INSERT INTO lap_times VALUES (2, 'Alice', 40);
INSERT INTO lap_times VALUES (2, 'Bob', 38);
INSERT INTO lap_times VALUES (2, 'Claire', 39);
INSERT INTO lap_times VALUES (3, 'Alice', 25);
INSERT INTO lap_times VALUES (3, 'Bob', 23);
INSERT INTO lap_times VALUES (3, 'Claire', 24);
INSERT INTO lap_times VALUES (4, 'Alice', 65);
INSERT INTO lap_times VALUES (4, 'Bob', 67);
INSERT INTO lap_times VALUES (4, 'Claire', 66);
我们想创建一些任意的类别:
CREATE TABLE categories (id INTEGER, name TEXT);
INSERT INTO categories VALUES (1, 'Running');
INSERT INTO categories VALUES (2, 'Swimming');
INSERT INTO categories VALUES (3, '100 meter');
让我们的运动成员属于这些类别之一:
CREATE TABLE memberships (category_id INTEGER, member_type TEXT, member_id INTEGER);
INSERT INTO memberships VALUES (1, 'Sport', 1);
INSERT INTO memberships VALUES (1, 'Sport', 2);
INSERT INTO memberships VALUES (2, 'Sport', 3);
INSERT INTO memberships VALUES (2, 'Sport', 4);
INSERT INTO memberships VALUES (3, 'Sport', 1);
INSERT INTO memberships VALUES (3, 'Sport', 4);
我们希望有一个“超级”分类,包含其他分类:
INSERT INTO categories VALUES (4, 'Running + Swimming');
INSERT INTO memberships VALUES (4, 'Category', 1);
INSERT INTO memberships VALUES (4, 'Category', 2);
现在来到了棘手的部分。
我们想要根据每项运动员的圈速排名:
SELECT sport_id, athlete,
RANK() over(PARTITION BY sport_id ORDER BY seconds)
FROM lap_times lt;
但是我们还希望在类别级别上这样做。当我们这样做时,运动员的排名应基于其在该类别中所有运动项目中的平均排名。例如:
Alice is 1st in 100 meter sprint and 3rd in 400 meter sprint
-> average rank: 2
Bob is 2nd in 100 meter sprint and 1st in 400 meter sprint
-> average rank: 1.5
Claire is 3rd in 100 meter sprint and 2nd in 400 meter sprint
-> average rank: 2.5
Ranking for running: 1st Bob, 2nd Alice, 3rd Claire
对于“超级”类别,运动员的排名应基于他们在各类别中的平均排名,而不是这些类别内部的运动项目。即仅应考虑其直接子类别,而不是展开所有运动项目。
我尽力编写了一个查询来计算这些排名。它是一个递归查询,从底层运动项目开始,通过会员资格向上计算类别和“超级”类别的排名。以下是我的查询:
WITH RECURSIVE rankings(rankable_type, rankable_id, athlete, value, rank) AS (
SELECT 'Sport', sport_id, athlete, seconds, RANK() over(PARTITION BY sport_id ORDER BY seconds)
FROM lap_times lt
UNION ALL
SELECT 'Category', category_id, athlete, avg(r.rank), RANK() OVER (PARTITION by category_id ORDER BY avg(r.rank))
FROM categories c
JOIN memberships m ON m.category_id = c.id
JOIN rankings r ON r.rankable_type = m.member_type AND r.rankable_id = m.member_id
GROUP BY category_id, athlete
)
SELECT * FROM rankings;
但是,当我运行它时,收到以下错误:
ERROR: aggregate functions are not allowed in a recursive query's recursive term
这是由查询的递归部分中的avg(r.rank)
引起的。PostgreSQL不允许在查询的递归部分中调用聚合函数。有没有其他方法可以编写这个查询?如果我将
avg(r.rank), RANK() ...
替换为NULL, NULL
,则查询会执行,对于运动项目,结果看起来正确,并且包含类别的预期行数。我考虑尝试使用嵌套查询将递归展开到两个或三个级别,因为这对我的用例来说没问题,但我想在尝试之前先在这里问一下。
另一个选择可能是更改模式,使得运动项目不能属于多个类别。我不确定在那种情况下查询会是什么样子,但可能会更简单?
非常感谢您的帮助。
with recursive cat_tree as (...), aggregates as (...) select * from aggregates
。 - user330315