Rails:为什么where(id:objects)有效?

时间:2018-03-15 10:20:30

标签: ruby-on-rails

我有以下声明:

Customer.where(city_id: cities)

导致以下SQL语句:

SELECT customers.* FROM customers WHERE customers.city_id IN (SELECT cities.id FROM cities...

这是预期的行为吗?它在某处记录了吗?我不会使用上面的Rails代码,而是使用以下其中一个:

Customer.where(city_id: cities.pluck(:id))

Customer.where(city: cities)

导致完全相同的SQL语句。

2 个答案:

答案 0 :(得分:7)

AREL查询库允许您将ActiveRecord对象作为快捷方式传递。然后它会将其主键属性传递给它用于联系数据库的SQL。

在查找多个对象时,AREL库将尝试在尽可能少的数据库往返中查找信息。它通过将您正在进行的查询作为一组条件来执行此操作,直到检索对象为止。

这种方式效率低下:

users = User.where(age: 30).all
#                           ^^^ get all these users from the database
memberships = Membership.where(user_id: users)
#                                       ^^^^^ This will pass in each of the ids as a condition

基本上,这种方式会发出两个SQL语句:

select * from users where age = 30;
select * from memberships where user_id in (1, 2, 3);

每个都涉及在应用程序和数据之间的网络端口上调用,然后通过同一端口传回。

这会更有效:

users = User.where(age: 30)
# This is still a query object, it hasn't asked the database for the users yet.
memberships = Membership.where(user_id: users)
# Note: this line is the same, but users is an AREL query, not an array of users

它将构建一个单独的嵌套查询,因此它只需要对数据库进行一次往返。

select * from memberships 
where user_id in (
  select id from users where age = 30
);

所以,是的,这是预期的行为。它有点像Rails魔术,它旨在提高应用程序的性能,而无需了解它的工作原理。

还有一些很酷的优化,例如,如果您拨打firstlast而不是all,它只会检索一条记录。

User.where(name: 'bob').all
# SELECT "USERS".* FROM "USERS" WHERE "USERS"."NAME" = 'bob'
User.where(name: 'bob').first
# SELECT "USERS".* FROM "USERS" WHERE "USERS"."NAME" = 'bob' AND ROWNUM <= 1

或者,如果你设置了一个订单,然后拨打最后一个,那么它将反转订单,然后只抓住列表中的最后一个(而不是抓住所有记录,只给你最后一个记录)。

User.where(name: 'bob').order(:login).first
# SELECT * FROM (SELECT "USERS".* FROM "USERS" WHERE "USERS"."NAME" = 'bob' ORDER BY login) WHERE ROWNUM <= 1
User.where(name: 'bob').order(:login).first
# SELECT * FROM (SELECT "USERS".* FROM "USERS" WHERE "USERS"."NAME" = 'bob' ORDER BY login DESC) WHERE ROWNUM <= 1
# Notice, login DESC

答案 1 :(得分:5)

  

为什么会这样?

ActiveRecord查询构建器中的某些内容非常智能,可以看到如果传递数组或查询/条件,则需要构建IN子句。

  

这是否记录在任何地方?

是的,http://guides.rubyonrails.org/active_record_querying.html#hash-conditions

2.3.3子集条件

  

如果要使用IN表达式查找记录,可以将数组传递给条件哈希:

Client.where(orders_count: [1,3,5])
     

此代码将生成如下SQL:

SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))