Rails 5具有多个子查询的复杂查询

时间:2018-06-15 01:35:35

标签: ruby-on-rails postgresql activerecord

我使用postgres在rails 5上,我有一个用户表和一个报告表。用户有很多报告,这些报告需要每天创建。我想获取所有尚未归档但尚未完成报告的用户,并显示昨天的报告说明(如果有)。

以下是模型:

用户模型

# == Schema Information
#
# Table name: users
#
#  id         :bigint(8)        not null, primary key
#  name       :string           not null
#  created_at :datetime         not null
#  updated_at :datetime         not null
#  archived   :boolean          default(FALSE)
#

class User < ApplicationRecord
  has_many :reports
end

报告模型

# == Schema Information
#
# Table name: reports
#
#  id         :bigint(8)        not null, primary key
#  notes      :text
#  created_at :datetime         not null
#  updated_at :datetime         not null
#  user_id    :bigint(8)
#

class Report < ApplicationRecord
  belongd_to :user
end

以下是我希望从此查询中获得的示例:

用户表

------------------------------------------------------------------------------------
| id |  name  | archived |         created_at         |         updated_at         |
------------------------------------------------------------------------------------
| 1  |  Jonn  |  false   | 2018-05-11 00:01:36.124999 | 2018-05-11 00:01:36.124999 |
------------------------------------------------------------------------------------
| 2  |  Sam   |  false   | 2018-05-11 00:01:36.124999 | 2018-05-11 00:01:36.124999 |
------------------------------------------------------------------------------------
| 3  | Ashley |  true    | 2018-05-11 00:01:36.124999 | 2018-05-11 00:01:36.124999 |
------------------------------------------------------------------------------------

报告表(想象一下这个报告是昨天)

--------------------------------------------------------------------------------------
| id |  user_id  |  notes  |         created_at         |         updated_at         |
--------------------------------------------------------------------------------------
| 1  |     1     | Nothing | 2018-06-13 16:32:05.139284 | 2018-06-13 16:32:05.139284 |
--------------------------------------------------------------------------------------

欲望输出:

-------------------------------------------------------------------------------------------------------
| id |  name  | archived |         created_at         |         updated_at         | yesterdays_notes |
-------------------------------------------------------------------------------------------------------
| 1  |  Jonn  |  false   | 2018-05-11 00:01:36.124999 | 2018-05-11 00:01:36.124999 |     Nothing      |
-------------------------------------------------------------------------------------------------------
| 2  |  Sam   |  false   | 2018-05-11 00:01:36.124999 | 2018-05-11 00:01:36.124999 |       NULL       |
-------------------------------------------------------------------------------------------------------

我能够在编写原始SQL时获得所需的查询结果,但是我在尝试将其转换为活动记录查询时遇到了很多问题。这是否适合使用scenic gem

以下是原始SQL查询:

SELECT u.*, (
  SELECT notes AS yesterdays_notes
  FROM reports AS r
  WHERE r.created_at >= '2018-06-13 04:00:00'
  AND r.created_at <= '2018-06-14 03:59:59.999999'
  AND r.user_id = u.id
)
FROM users AS u
WHERE u.archived = FALSE
AND u.id NOT IN (
  SELECT rr.user_id
  FROM reports rr
  WHERE rr.created_at >= '2018-06-14 04:00:00'
);

3 个答案:

答案 0 :(得分:2)

我将如何做到这一点:

首先,选择昨天创建的最新报告的所有活动用户,并将其分配给var:

users = User.where(archived: false).joins(:reports)
.where.not('DATE(reports.created_at) IN (?)', [Date.today]) 
.where('DATE(reports.created_at) IN (?)', [Date.yesterday])
.select('users.id', 'users.name', 'notes')

现在,用户var将.select中列出的attrs可用,因此您可以致电users.map(&:notes)查看节点列表,包括nil / null备注。< / p>

另一个可能派上用场的技巧是能够为.select中列出的阁楼添加别名。例如,如果您想将users.id存储为id,则可以使用

执行此操作
...
.select('users.id as id', 'users.name', 'reports.notes')

你可以致电users.map(&:attributes)看看这些最终结构是什么样的

可以找到有关可用Active Record查询的更多信息here

答案 1 :(得分:1)

users = User.joins(:reports).where("(reports.created_at < ? OR reports.created_at BETWEEN ? AND ?) AND (users.archived = ?)", Date.today.beginning_of_day, Date.yesterday.beginning_of_day, Date.yesterday.end_of_day, false).select("users.id, users.name, reports.notes").uniq

用户将返回#<ActiveRecord::Relation [....]

可能联接返回重复记录,因此请使用uniq

过滤报告

reports.created_at < Date.today.beginning_of_day OR yesterday.beginning_of_day > reports.created_at < Yesterday.end_of_day

需要报告为&#34;今天未完成报告,并显示昨天的报告说明(如果有)&#34;

users.archived = false

答案 2 :(得分:0)

我能够从这里发布的2个答案中得到所需的结果,但是,在做了一些更多的研究后,我认为使用scenic gem使用数据库视图是合适的方法,所以我将继续前进那个。

感谢您的投入!如果你想看看我的决定背后的一些推理,这个stackoverflow帖子很好地总结了它:https://stackoverflow.com/a/4378166/2909095

以下是我最终使用scenic gem的内容。我更改了实际的查询以更好地满足我的需求,但它解决了这个问题:

模型

# == Schema Information
#
# Table name: missing_reports
#
#  id                  :bigint(8)
#  name                :string
#  created_at          :datetime
#  updated_at          :datetime
#  previous_notes      :text
#  previous_notes_date :datetime

class MissingReport < ApplicationRecord
end

数据库视图

SELECT u.*, rs.notes AS previous_notes, rs.created_at AS previous_notes_date
FROM users AS u
LEFT JOIN (
  SELECT r.*
  FROM reports AS r
  WHERE r.created_at < TIMESTAMP 'today'
  ORDER BY created_at DESC
  LIMIT 1
) rs ON rs.user_id = u.id
WHERE u.archived = FALSE
AND u.id NOT IN (
  SELECT rr.user_id
  FROM standups rr
  WHERE rr.created_at >= TIMESTAMP 'today'
);

用法

def index
  @missing_reports = MissingReport.all
end