查找不存在关联记录的父记录

这是我所拥有的协会。

award_test.rb

has_many :instructor_student_awards , :dependent => :destroy has_many :award_test_payment_notifications, dependent: :destroy 

instructor_student.rb

 has_many :instructor_student_awards, :dependent => :destroy has_many :awards, through: :instructor_student_awards has_many :award_test_payment_notifications, dependent: :destroy 

instructor_student_award.rb

 belongs_to :award belongs_to :instructor_student belongs_to :award_test 

award_test_payment_notification.rb

 belongs_to :award_test belongs_to :instructor_student 

schema.rb

 "award_tests" t.date "test_date" t.time "test_time" t.integer "award_id" t.decimal "test_fee" "award_test_payment_notifications" t.text "response_params" t.string "status" t.string "transaction_id" t.string "paid_by" t.float "amount" t.string "merchant_reference" t.integer "award_test_id" t.integer "instructor_student_id" "instructor_student_awards" t.integer "instructor_student_id" t.integer "award_test_id" t.boolean "is_registered", default: false 

任何教师学生都可以在奖励考试中注册。 一名教师学生可以参加多项奖励考试。 教师学生必须支付考试费用。 教师学生可以通过2种方式进行奖励测试。

  1. 线上

  2. 现金

当讲师学生为该奖项考试付费时。 比讲师学生和award_test数据将插入award_test_payment_notifications 。 award_test_notifications表具有与学生和奖励测试相关的所有领域。

如果教师学生不支付该奖励测试而不是未付费,那么奖励测试支付通知将仅在该奖励测试中为零。

指导学生1注册3个奖项测试,如10,11,12。

指导学生1支付现金进行奖励测试10。

指导学生1在线支付奖励测试12。

指导学生1没有支付奖励测试11。

因此,教师学生有2个奖励测试支付通知1)奖励测试10 2)奖励测试12

当我在2017年1月1日至2017年7月31日期间搜索奖励测试时。这3个奖项属于此filter,而讲师学生1将返回3个类别。

  1. 现金支付

  2. 在线支付

  3. 未付

我有2个日期,我需要根据2个日期过滤数据。 该日期将是测试日期。 我必须区分所有现金支付,在线支付和未支付。

以下是我为获得现金支付和在线支付所做的查询。

 current_admin_award_test = AwardTest.joins(instructor_student_awards: {:instructor_student => :award_test_payment_notifications}).where("test_date = ? AND instructor_student_awards.instructor_student_id IN (?) ", @endMonth.end_of_month, @startMonth.beginning_of_month, @all_instructor_student_ids) @award_test_cash = current_admin_award_test.joins("INNER JOIN award_test_payment_notifications atn ON award_test_payment_notifications.award_test_id = award_tests.id").where("award_test_payment_notifications.paid_by = ? ","Cash").select("DISTINCT(award_tests.id), instructor_student_awards.instructor_student_id, award_test_payment_notifications.paid_by, award_tests.test_fee, award_tests.test_date") @award_test_paid_online = current_admin_award_test.joins("INNER JOIN award_test_payment_notifications atn ON award_test_payment_notifications.award_test_id = award_tests.id").where("award_test_payment_notifications.paid_by  ? ","Cash").select("DISTINCT(award_tests.id), instructor_student_awards.instructor_student_id, award_test_payment_notifications.paid_by, award_tests.test_fee, award_tests.test_date") 

它工作正常。 但我被困在如何获得一个教练学生多次奖励测试的无偿奖励测试列表。

更新1

InstructorStudentAward的作用是什么?

当讲师学生注册奖励考试时。 记录在教师学生奖励中的数据。

如果为该AwardTest记录了任何付款,则假定该事件已全额付款。 好?

是。 当讲师学生支付奖励考试时。 奖励测试付款通知将被记录,无论金额是多少。

是否为每个注册AwardTest的InstructorStudent创建了一个InstructorStudentAward关联?

是的,你是对的 。

所以你想要的是,任何缺少相关的AwardTestPaymentNotification的InstructorStudentAward JOIN AwardTest JOIN InstructorStudent对

我可以假设您正在使用PostgreSQL数据库

是的,我正在使用PostgreSQL数据库。

我添加了这个github repo,其中包含我需要的部分问题(以及一些你没有提及的内容)来复制你的关联结构。

https://github.com/kingdonb/ashvin-stackoverflow-post

项目根目录中的example.rb是一个Rails Runner脚本,我想这些脚本大致会复制这些东西是如何创建和链接在一起的。

您的查询,重新格式化为有点可读:

 current_admin_award_test = AwardTest.joins( instructor_student_awards: { :instructor_student => :award_test_payment_notifications }).where("test_date <= ? AND test_date >= ? AND instructor_student_awards.instructor_student_id IN (?) ", @endMonth.end_of_month, @startMonth.beginning_of_month, @all_instructor_student_ids) @award_test_cash = current_admin_award_test.joins( "INNER JOIN award_test_payment_notifications atn ON award_test_payment_notifications.award_test_id = award_tests.id"). where( "award_test_payment_notifications.paid_by = ? ", "Cash" ).select( "DISTINCT(award_tests.id), instructor_student_awards.instructor_student_id, award_test_payment_notifications.paid_by, award_tests.test_fee, award_tests.test_date") @award_test_paid_online = current_admin_award_test.joins( "INNER JOIN award_test_payment_notifications atn ON award_test_payment_notifications.award_test_id = award_tests.id"). where( "award_test_payment_notifications.paid_by <> ? ","Cash" ).select( "DISTINCT(award_tests.id), instructor_student_awards.instructor_student_id, award_test_payment_notifications.paid_by, award_tests.test_fee, award_tests.test_date") 

我认为缺少的查询,如果你打算这样做,就不能从current_admin_award_test派生出来,因为它默默地做了一个内连接。 当你说没有附加上下文的“连接”时,Rails推断你的意思是内连接: https : //apidock.com/rails/ActiveRecord/QueryMethods/joins#docs_body

看看这个结果:

 current_admin_award_test = AwardTest.joins( instructor_student_awards: { :instructor_student => :award_test_payment_notifications }) 

你只收回付费的InstructorStudentAwards!

因此,它会丢弃没有匹配的AwardTestPaymentNotifications的记录,因为内部联接需要两侧匹配才能生成行结果。 因此,如果从该关联对象开始查询,它将不会返回任何未付的记录。

你要:

 @award_test_unpaid = AwardTest.left_outer_joins( instructor_student_awards: { instructor_student: :award_test_payment_notifications }).where(award_test_payment_notifications: { id: nil } ). [ and the rest of your criteria from current_admin_award_test ] where("test_date <= ? AND test_date >= ? AND instructor_student_awards.instructor_student_id IN (?) ", @endMonth.end_of_month, @startMonth.beginning_of_month, @all_instructor_student_ids) 

如果您使用的是旧版本的Rails,则会变得更复杂,因为#left_outer_joins仅在Rails 5.0中添加。

如果你想改进这一点,你可以将current_admin_award_test变成一个参数化的范围,该范围不会加入award_test_payment_notifications (你已经加入该表来制作每个派生的付款状态列表,我担心这会加入两次,这可能是你必须加入的原因求助于使用DISTINCT ……?还是有其他原因?)

也许他们为一个AwardTest有多个AwardTestPaymentNotifications,你不想从那里获得重复的AwardTest记录。 好。 您也可以使用此新范围作为@award_test_unpaid的基础。

 scope :current_admin_award_test, ->( end_date, begin_date, instructor_student_list) { joins(instructor_student_awards: :instructor_student). where("test_date <= ? AND test_date >= ?", end_date.end_of_month, begin_date.beginning_of_month). where(instructor_student_awards: { instructor_student: instructor_student_list } ) } 

( 在 GitHub项目的上下文中)我把它放在了AwardTest模型的提交中。

您的三个私有变量也可以是作用域,具有相同的参数(将它们传递给此主作用域,处理日期逻辑。)

(在给出疑问的好处之内,你可能已经在你的模型中有了这些东西,但是根本就没有将它们包含在问题中,因为你不想在其中加入过多的代码。)

因此,如果InstructorStudent尚未支付其中一张账单,他的AwardTest将显示在此列表中。 如果所有账单都已付清,那么InstructorStudent将不会出现在此列表中。 我想这就是你想要实现的目标?

如果你想进一步派出一个有未支付账单的InstructorStudent记录列表,那么你可以发送每个强势措辞的电子邮件,现在它看起来像这样:

 @award_test_unpaid. map(&:instructor_student_awards).flatten. map(&:instructor_student). map(&:id).uniq 

为了获得奖金,我只是花了一些时间在我站起来的repo上,充实了rails关联和范围,以便有一个current_unpaid范围,大致与你想要的一样。

 scope :current_unpaid, ->( end_date, begin_date, instructor_student_list) { current_admin_award_test(end_date, begin_date, instructor_student_list). left_outer_joins(:award_test_payment_notifications). where(award_test_payment_notifications: { id: nil } ) } 

2017-08-04编辑:@ashvin透露他是在Rails 4.1上,所以.left_outer_join不起作用,因为直到Rails 5才添加…这个原始SQL将在它的位置工作,但是:

  scope :current_unpaid, ->( end_date, begin_date, instructor_student_list) { current_admin_award_test(end_date, begin_date, instructor_student_list). joins("LEFT JOIN award_test_payment_notifications ON award_tests.id = award_test_payment_notifications.award_test_id AND instructor_students.id = award_test_payment_notifications.instructor_student_id"). where(award_test_payment_notifications: { id: nil } ) } 

然后你可以

 AwardTest.current_unpaid(Date.today, Date.today, InstructorStudent.all.map(&:id)) 

并获取当月所有未付奖励测试的列表!

这取决于在AwardTest模型上设置has_many :award_test_payment_notifications 。 它还取决于我们在上面进一步定义的current_admin_award_test范围。 使这项工作的所有更改都在此提交中: 更多时间在rails关联上 。 希望这可以帮助!