繁体   English   中英

在 ActiveRecord 中包含 where 子句

[英]includes with where clause in ActiveRecord

我想预加载关联,但如果不存在关联,我想返回主要关系。

要在这里理解我的问题是一个可重现的脚本:

# frozen_string_literal: true
gem 'rails'
require 'active_record'

puts "Active Record #{ActiveRecord::VERSION::STRING}"

ActiveRecord::Base.establish_connection(
  adapter: 'sqlite3',
  database: ':memory:'
)

ActiveRecord::Schema.define do
  create_table :organisations, force: true do |t|
    t.string 'name', limit: 255, null: false
    t.datetime 'created_at'
    t.datetime 'updated_at'
  end

  create_table :groups, force: true do |t|
    t.string 'name', limit: 255, null: false
    t.integer 'shop_id', null: false
    t.datetime 'created_at'
    t.datetime 'updated_at'
  end

  create_table :groups_organisations, force: true do |t|
    t.integer  'organisation_id', null: false
    t.integer  'group_id', null: false
    t.datetime 'created_at'
    t.datetime 'updated_at'
  end
end

class Organisation < ActiveRecord::Base
  has_many :groups_organisations
  has_many :groups, through: :groups_organisations
end

class Group < ActiveRecord::Base
  has_many :groups_organisations
  has_many :organisation, through: :groups_organisations
end

class GroupsOrganisation < ActiveRecord::Base
  belongs_to :group
  belongs_to :organisation
end

organisation_without_groups = Organisation.create!(name: 'my organisation without groups')
group_1 = Group.create!(name: 'group_1', shop_id: 1)
group_2 = Group.create!(name: 'group_2', shop_id: 2)
organisation_with_groups = Organisation.create!(name: 'my organisation with groups', groups: [group_1, group_2])

当我没有要包含的记录时

p Organisation.where(name: 'my organisation without groups').includes(:groups).where(groups: { shop_id: 1 })
# <ActiveRecord::Relation []>

当我有记录要包括时

p Organisation.where(name: 'my organisation with groups').includes(:groups).where(groups: { shop_id: 1 })
# <ActiveRecord::Relation [#<Organisation id: 2, name: "my organisation with groups", created_at: "2017-07-21 09:45:07", updated_at: "2017-07-21 09:45:07">]>

我得到的 SQL 是

puts Organisation.where(name: 'my organisation witReproduce the actual bug!hout groups').includes(:groups).where(groups: { shop_id: 1 }).to_sql

对不在 LEFT OUTER JOIN 上的所有查询执行 where 子句

SELECT "organisations"."id" AS t0_r0,
       "organisations"."name" AS t0_r1,
       "organisations"."created_at" AS t0_r2,
       "organisations"."updated_at" AS t0_r3,
       "groups"."id" AS t1_r0,
       "groups"."name" AS t1_r1,
       "groups"."shop_id" AS t1_r2,
       "groups"."created_at" AS t1_r3,
       "groups"."updated_at" AS t1_r4
FROM "organisations"
LEFT OUTER JOIN "groups_organisations" 
             ON "groups_organisations"."organisation_id" = "organisations"."id"
LEFT OUTER JOIN "groups" 
             ON "groups"."id" = "groups_organisations"."group_id"
WHERE "organisations"."name" = 'my organisation without groups'
  AND "groups"."shop_id" = 1

我想要的是

LEFT OUTER JOIN "groups" 
             ON "groups"."id" = "groups_organisations"."group_id"
            AND groups.store_id = 20441
WHERE "organisations"."name" = 'my organisation without groups'

是否可以使用带有条件的 LEFT OUTER JOIN 进行预加载?

您要选择 groups.shop_id 为 1 或没有 groups.shop_id 的位置

你可以通过对nil的测试来做到这一点

Organisation.where(name: 'my organisation without groups').includes(:groups).where(groups: { shop_id: [nil, 1] })

对于一般方法,有关.includes() 文档非常有用。

特别是关于条件的一点:

如果要将字符串条件添加到包含的模型中,则必须显式引用它们。 例如:

 User.includes(:posts).where('posts.name = ?', 'abcd')

会抛出一个错误,但这会起作用

 User.includes(:posts).where('posts.name = ?', 'abcd').references(:posts)

请注意,包括使用关联名称,而引用需要实际的表名称。

如果您通过哈希传递条件,则无需显式调用引用,因为 where 为您引用表。 例如,这将起作用

 User.includes(:posts).where(posts: { name: 'abcd' })


更进一步,如果你有

thing.other_things.includes(:even_more_things)
.where(other_things: {col1: 'value'})

并且您想过滤even_more_things ,您可以使用:

thing.other_things.joins(:even_more_things)
.where(even_more_things: { attribute: value })

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM