【问题标题】:ActiveRecord: Scope to get parents with no children created at a specific dateActiveRecord:在特定日期创建没有孩子的父母的范围
【发布时间】:2016-08-12 05:20:43
【问题描述】:

我想在 Rails 4 中以 ActiveRecord 和 Postgres 作为数据库的一对多关系获取在特定日期创建的 no 记录的所有父记录。

迁移:

class CreateParents < ActiveRecord::Migration
  def change
    enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')

    create_table :parents, id: :uuid, default: 'gen_random_uuid()' do |t|
      t.string :name
      t.timestamps null: false
    end
end

class CreateChilds < ActiveRecord::Migration
  def change
    create_table :childs, id: false do |t|
      t.uuid :parent_id, null: false
      t.date :created_at, null: false
      t.string :name
    end

    add_foreign_key :childs, :parents
    add_index :childs, [:parent_id, :created_at], :unique => true
  end
end

型号:

class Parent < ActiveRecord::Base
  has_many :childs
end

class Child < ActiveRecord::Base
  belongs_to :parent
end

现在我想让所有在特定日期没有孩子的父母都有一个范围:

class Parent < ActiveRecord::Base
  has_many :childs

  def self.with_no_childs_created_at(date)
    ...
  end
end

有人可以帮我吗?我真的要疯了。我用.includes.references.where.not.joins 等尝试了很多东西,但我不明白。


更新 1

一个建议的解决方案如下所示:

def self.with_no_stats_created_at(date)
  joins(:childs).where.not(childs: {created_at: date})
end

但这仅适用于父级过去已经创建了一个子级的情况。 SQL 应该能说明问题:

SELECT "parents".*
FROM "parents"
INNER JOIN "childs" ON "childs"."parent_id" = "parents"."id"
WHERE ("childs"."created_at" != $1)  [["created_at", "2016-04-19"]]

更新 2

这解决了问题(@Ilya 建议):

def self.with_no_childs_created_at(date)
  preload(:childs).select {|p| p.childs.all? {|c| c.created_at != date }}
end

【问题讨论】:

  • “没有孩子的父母”这个概念好奇怪,看到你的标题我笑死了!谢谢!
  • 不幸的是,没有父母的孩子很常见。但直到现在还没有听说过相反的事情!

标签: ruby-on-rails ruby postgresql ruby-on-rails-4 activerecord


【解决方案1】:

您可以完全在数据库中完成,大多数时候速度更快:

scope :without_children_at(date) = {
  joins(:childs).where("DATE(created_at) != DATE(?)", date)
}

【讨论】:

    【解决方案2】:

    您可以预加载子元素以避免N+1 查询并像数组一样处理它:

    def self.with_no_childs_created_at(date)
      preload(:childs).select {|p| p.childs.all? {|c| c.created_at != date }}
    end
    

    【讨论】:

    • 感谢您的快速回复!但您的解决方案只有在父母过去已经创建了一个孩子的情况下才有效。
    • @JohnDoe 您期望的具体行为是什么?能写个实例吗?
    • 简短示例 - 三个父记录:第一个没有孩子,第二个昨天创建了一个孩子,第三个今天创建了一个孩子。我想用Parent.with_no_childs_created_at(Date.today) 获得第一个和第二个父母。
    • 非常感谢!这解决了我的问题并节省了我的一天:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    • 2015-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多