【问题标题】:Select unique records and display as category headers in rails选择唯一记录并在 rails 中显示为类别标题
【发布时间】:2013-04-01 13:50:18
【问题描述】:

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

+----+--------+------------------+--------------------+
| 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 排序,看起来应该像 ...

2013-03-01 – 检查碎片 托比 2013-03-02 – 开展Y1.1 2013-03-03 – 检查油封

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

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

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

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

【问题讨论】:

  • 我认为您的第一个问题是您的数据库似乎没有正确规范化。除了tasks,您可能还应该有一个mechanicstechnicians 表,其中存储了BenToby。然后,在tasks 中,不是有一个name 列,而是有一个引用mechanicmechanic_id 列。
  • 然后你可以这样做:在你的控制器中,设置@mechanics = Mechanic.all,然后在视图中,设置<% @mechanics.each do |mechanic| %><%= mechanic.name %><% mechanic.tasks.each do |task| %><%= task.description %><% end %><% end%>。 (我还建议将tasks.task 更改为tasks.description。)
  • 哦,我的意思是在我的第一条评论中链接这个:databases.about.com/od/specificproducts/a/normalization.htm

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


【解决方案1】:

要按照您的描述对数据进行排序,您可能希望在ORDER BY 子句中使用min() 作为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;

我还添加了 task (3) 作为最后一个 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

【讨论】:

  • 嗨 Erwin,我并不是说查询的输出必须在 1 列中,这就是它在 ERB 中呈现的方式。不过,您的回答很有趣。我正在尝试学习更多原始 sql,所以这是非常有价值的信息。谢谢
  • @Chris:我只能处理实际in 你的问题。添加一个结果应该是什么样子的例子总是有帮助的。此外,我将缺少的任务添加到我的查询中。我还添加了有关您的原始查询出了什么问题的信息。
  • 感谢欧文的编辑,我会投票赞成你的答案,但我没有足够的代表。
【解决方案2】:

假设您的模型中有关联,您将能够运行

@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

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

【讨论】:

  • 是的,这大致是正确的做法。一个区别是,从某种角度来看,它将是 ERB 而不是直接的 Ruby,我认为做这些事情的先决条件是让你的表正确规范化,据我所知,它们几乎肯定还没有。
  • 我同意,改进的标准化肯定是一个需要改进的地方。
  • 谢谢杰森,羊肉,米莎。数据来自 Excel 表,所以我认为编写更复杂的查询比更改解析器更快,但是我认为我会采纳您的建议并规范化我的数据库。如果您知道如何将 Q 中的 SQL 查询转换为 Active Record,我仍然会感兴趣。再次感谢您的回复!
猜你喜欢
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 2011-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多