选择唯一记录并在rails中显示为类别标题

时间:2013-04-01 13:50:19

标签: sql ruby-on-rails postgresql activerecord sql-order-by

我在PostgreSQL上运行了一个rails 3.2 app,并且我想在我的视图中显示一些数据,这些数据以这种结构存储在数据库中:

+----+--------+------------------+--------------------+
| id | name   | sched_start_date | task               |
+----+--------+------------------+--------------------+
| 1  | "Ben"  | 2013-03-01       | "Check for debris" |
+----+--------+------------------+--------------------+
| 2  | "Toby" | 2013-03-02       | "Carry out Y1.1"   |
+----+--------+------------------+--------------------+
| 3  | "Toby" | 2013-03-03       | "Check oil seals"  |
+----+--------+------------------+--------------------+

我想显示每个名称的任务列表,以及他们所拥有的第一个ASC要订购的名称sched_start_date,这应该是......

Ben
2013-03-01 – Check for debris

Toby
2013-03-02 – Carry out Y1.1
2013-03-03 – Check oil seals

我开始采用的方法是运行查询唯一名称并按sched_start_date ASC排序,然后为每个名称运行查询以获取其任务。

要获取唯一名称列表,SQL将如下所示。

select * 
from (
   select distinct on (name) name, sched_start_date 
   from tasks
) p 
order by sched_start_date;

我想知道这是否是正确的方法(查询唯一名称然后为其所有任务运行另一个查询),或者是否有更好的rails方式。

2 个答案:

答案 0 :(得分:1)

要按照您的描述对数据进行排序,您可能希望在min()子句中使用ORDER BY作为window function

SELECT name, sched_start_date, task
FROM   tasks
ORDER  BY min(sched_start_date) OVER (PARTITION BY name), 1, 2, 3

您的原始查询需要额外的ORDER BY项才能获得每个名称的最早日期:

SELECT DISTINCT ON (name) name, sched_start_date, task
FROM   tasks
ORDER  BY 1, 2, 3;

我还添加task3)作为最后ORDER BY项以打破关系,以防每个日期可以有多个。

但输出仍按name排序,而不是date

将所有数据填入一列的特殊格式有点复杂:

SELECT one_col
FROM  (
    WITH x AS (
        SELECT name, min(sched_start_date) AS min_start
        FROM   tasks
        GROUP  BY 1
        )
    SELECT 2 AS rnk, name
          ,sched_start_date::text || ' – ' || task AS one_col
          ,sched_start_date, min_start
    FROM   tasks
    JOIN   x USING (name)

    UNION ALL
    SELECT 1 AS rnk, name, name, NULL::date, min_start
    FROM   x

    ORDER  BY min_start, name, rnk, sched_start_date, task
    ) y

答案 1 :(得分:0)

假设您的模型中有关联,您就可以运行

@employees = Employee.order(:name, :sched_start_date, :task).includes(:tasks)

然后你可以迭代它们:

@employees.each do |employee|
  employee.name
  employee.tasks.each do |task|
    task.name
  end
end

这不能完全符合您的需求,但应该告诉您从哪里开始。