【发布时间】:2018-08-23 14:16:06
【问题描述】:
我是 Rails 新手,我的第一个项目是为 Redmine 制作插件。我需要找出问题状态更改之间经过了多长时间。
我通过对每个状态更改执行以下操作来计算:
- 从创建日期减去之前状态更改的创建日期
- 如果这是问题的第一次状态更改,请减去问题的创建日期。
我在 PostgreSQL 中使用lag 函数编写了以下代码来访问前一行,这给了我想要的输出。
SELECT issues.subject, journals.id,
COALESCE(
(journals.created_on - lag(journals.created_on)
OVER (PARTITION BY journals.journalized_id ORDER BY journals.created_on)),
journals.created_on - issues.created_on
) AS time_elapsed
FROM journals
INNER JOIN journal_details ON journal_details.journal_id = journals.id
INNER JOIN issues ON issues.id = journals.journalized_id
WHERE journal_details.prop_key = 'status_id';
...在 Rails 中我做到了...
Journal.joins(:details, :issue)
.where(:journal_details => {prop_key: :status_id})
.select("issues.subject, journals.id")
但是,为了选择上一行,我不能使用 Postgres 的函数,因为我需要此代码与数据库无关。
搞了几个小时,我只能想出一个选择前一行的子查询的想法:
subquery = "SELECT j.created_on FROM journals AS j
INNER JOIN journal_details ON journal_details.journal_id = j.id
WHERE journal_details.prop_key = 'status_id'
AND j.journalized_id = journals.journalized_id
AND j.id < journals.id ORDER BY j.id DESC LIMIT 1"
Journal.joins(:details, :issue)
.where(:journal_details => {prop_key: :status_id})
.select("issues.subject, journals.id,
COALESCE(journals.created_on - (#{subquery}),
(journals.created_on - issues.created_on)) AS time_elapsed")
这不是一个可行的选项,因为它对每一行执行一个多选择查询,我需要一个性能有效的解决方案。 (也不是 DRY。)
所以我的问题是,如何使用活动记录查询界面(如果可以的话,当然)来选择上一行?或者至少,解决这个问题的最佳实践/rails 方式是什么?
【问题讨论】:
-
"为了选择上一行,我不能使用 Postgres 的函数,因为我需要此代码与数据库无关。" 函数
LAG()也适用于 MySQL 8.0 等最流行的 DBMS . Oracle 10g Release 2 (10.2)+(我相信可以更早),PostgreSQL 8.4+,SQL Server(MSSQL) 版本 2012+,SQLite 在我看来使用LAG()它与数据库无关 -
此外,如果您担心与数据库无关..
LIMIT仅适用于 MySQL、MariaDB(MySQL 的分支)和 PostgreSQL。所以你不用担心使用LAG()不是数据库不可知论者 -
如果您知道要在 SQL 中进行的查询,您也可以执行 raw query,或者发布一个您无法完全融入 ActiveRecord 语法的有效查询,因为这可能会得到更多有针对性的帮助。您也可以考虑在dba.stackexchange.com 上询问特定于 SQL(而不是 ORM)的问题。
标签: ruby-on-rails postgresql rails-activerecord redmine