Rails ActiveRecord where子句与.first vs .last在本地vs生产中

时间:2018-01-10 06:33:23

标签: ruby-on-rails ruby postgresql heroku query-performance

我使用Rails 5和Postgres。我有一个表名buckets,没有列被索引。在我的本地机器上,我创建了55,000个虚拟记录。我只需要获取一条记录,其中bucket_type等于PR且issued_date为nil(有7种不同的bucket_type,如PR,CA,US,EU,VN,BU和GY),所以我尝试了2个不同的查询,其中{ {1}}和一个.first,以查看哪一个表现得更快。

.last此查询需要大约1.5毫秒

Bucket.where({issued_date: nil, bucket_type: 'PR'}).first此查询大约需要6.5毫秒

然而,在生产(Heroku)时,在桶表中有210万条记录,结果却相反:

Bucket.where({issued_date: nil, bucket_type: 'PR'}).last此查询大约需要750毫秒

Bucket.where({issued_date: nil, bucket_type: 'PR'}).first此查询大约需要150毫秒

我有两个问题:

  1. 为什么Bucket.where({issued_date: nil, bucket_type: 'PR'}).last条款之后.first.last的效果在本地与制作上有所不同?
  2. 是否有比上述查询更好的方法来获取一条记录,其中bucket_type等于PR且issued_date为nil ,而无需向where或{{1}添加任何索引 }或两列?

1 个答案:

答案 0 :(得分:2)

我真的不能告诉你第一个问题的回答,但对于第二个问题, 好吧,你应该使用limit

让我们看看差异:

User.where(is_disabled: 0).first
# Oracle
SELECT * FROM ( 
  SELECT  "USERS".* FROM "USERS" WHERE "USERS"."IS_DISABLED" = :a1 
  ORDER BY "USERS"."ID" ASC 
) WHERE ROWNUM <= :a2
# MariaDB
SELECT  `users`.* FROM `users` WHERE `users`.`is_disabled` = 0 
ORDER BY `users`.`id` ASC LIMIT 1

你首先在oracle中看到所有用户都被选中,然后查询被包装到只选择第一个。这当然很糟糕,MySQL / MariaDB没有做到这一点。但是按user_id排序(井轨不是MariaDB)

User.where(is_disabled: 0).limit(1)
# Oracle
SELECT  "USERS".* FROM "USERS" WHERE "USERS"."IS_DISABLED" = :a1 
AND ROWNUM <= :a2 
#MariaDB
SELECT  `users`.* FROM `users` WHERE `users`.`is_disabled` = 1 LIMIT 1

在Oracle中,只有一个用户从一开始就被选中,这更快。 MariaDB不会对用户进行排序也会更快。

P.S。这可能会根据使用的数据库而改变,但无论如何都要limit。但只要我们不知道使用过的数据库,你的问题1就无法解决。此外,这应该分为两个问题