使用Postgres的SQL中的复杂排名

时间:2015-04-05 19:06:18

标签: sql ruby-on-rails postgresql

我已经掌握了复杂排名功能所需的SQL。这是一项适用于赛车运动的应用,我需要根据条目EntryTimesheet的{​​{1}}进行排名。

相关模型:

:total_time

条目class Timesheet has_many :entries end class Entry belongs_to :timesheet belongs_to :athlete end class Run belongs_to :entry end 未存储在数据库中。它是:total time的计算列。我使用Postgres(9.3)runs.sum(:finish)函数来获取给定时间表的条目,并按此计算列对它们进行排名。

rank()

到目前为止一切顺利。这将返回带有def ranked_entries Entry.find_by_sql([ "SELECT *, rank() OVER (ORDER BY total_time asc) FROM( SELECT Entries.id, Entries.timesheet_id, Entries.athlete_id, SUM(Runs.finish) AS total_time FROM Entries INNER JOIN Runs ON (Entries.id = Runs.entry_id) GROUP BY Entries.id) AS FinalRanks WHERE timesheet_id = ?", self.id]) end 属性的条目对象,我可以在rank上显示该属性。

现在是棘手的部分。在timesheet#show上,并非每个Timesheet都会有相同的次数。有一个截止点(通常是前20但不总是)。这使得Postgres的rank()不准确,因为有些参赛作品比竞赛获胜者的Entry更低,因为他们没有为第二次加热做出截止。

我的问题:是否可以在:total_time内执行类似rank()的操作,以生成如下所示的表格?或者还有另一种首选方式吗?谢谢!

注意:我将时间存储为整数,但为了清晰起见,我在下面的简化表中将它们格式化为更熟悉的MM:SS

rank()

1 个答案:

答案 0 :(得分:0)

让我们创建一个表。 (养成在所有SQL问题中包含CREATE TABLE和INSERT语句的习惯。)

create table runs (
  entry_id integer not null,
  run_num integer not null
    check (run_num between 1 and 3),
  run_time interval not null
);

insert into runs values
(1, 1, '00:59.33'),
(2, 1, '00:59.93'),
(3, 1, '01:03.27'),
(1, 2, '00:59.88'),
(2, 2, '00:59.27');

此SQL语句将按您所需的顺序为您提供总计,但不对其进行排名。

with num_runs as (
  select entry_id, count(*) as num_runs
  from runs
  group by entry_id
)
select r.entry_id, n.num_runs, sum(r.run_time) as total_time
from runs r
inner join num_runs n on n.entry_id = r.entry_id
group by r.entry_id, n.num_runs
order by num_runs desc, total_time asc
entry_id  num_runs  total_time
--
2         2         00:01:59.2
1         2         00:01:59.21
3         1         00:01:03.27

此语句添加了排名列。

with num_runs as (
  select entry_id, count(*) as num_runs
  from runs
  group by entry_id
)
select 
  rank() over (order by num_runs desc, sum(r.run_time) asc), 
  r.entry_id, n.num_runs, sum(r.run_time) as total_time
from runs r
inner join num_runs n on n.entry_id = r.entry_id
group by r.entry_id, n.num_runs
order by rank asc
rank  entry_id  num_runs  total_time
--
1     2         2         00:01:59.2
2     1         2         00:01:59.21
3     3         1         00:01:03.27