我有以下声明:
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语句。
答案 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魔术,它旨在提高应用程序的性能,而无需了解它的工作原理。
还有一些很酷的优化,例如,如果您拨打first
或last
而不是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
如果要使用IN表达式查找记录,可以将数组传递给条件哈希:
Client.where(orders_count: [1,3,5])
此代码将生成如下SQL:
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))