Rails ActiveRecord where 子句与 .first 与 .last 在本地与生产
Posted
技术标签:
【中文标题】Rails ActiveRecord where 子句与 .first 与 .last 在本地与生产【英文标题】:Rails ActiveRecord where clause with .first vs .last in local vs production 【发布时间】:2018-01-10 06:33:22 【问题描述】:我使用 Rails 5 和 Postgres。我有一个表名buckets
,没有一个列被索引。在我的本地机器上,我创建了 55,000 个虚拟记录。我只需要得到一条记录,bucket_type 等于 PR 并且 issue_date 为零(有 7 种不同的 bucket_type,例如 PR、CA、US、EU、VN、BU 和 GY),所以我尝试使用 @ 进行 2 个不同的查询987654322@ 和一个.last
看看哪个执行得更快。
Bucket.where(issued_date: nil, bucket_type: 'PR').first
这个查询大约需要 1.5ms
Bucket.where(issued_date: nil, bucket_type: 'PR').last
这个查询大约需要 6.5ms
但是,在生产环境 (Heroku) 中,buckets 表中有 210 万条记录,结果却相反:
Bucket.where(issued_date: nil, bucket_type: 'PR').first
这个查询大约需要 750 毫秒
Bucket.where(issued_date: nil, bucket_type: 'PR').last
这个查询大约需要 150 毫秒
我有两个问题:
-
为什么
.first
和 .last
在 where
子句之后的性能在本地和生产中不同?
有没有比上述查询更好的方法来获得一条记录,其中 bucket_type 等于 PR 并且 issue_date 为零 无需添加任何索引 到 bucket_type
或 issued_date
或两者列?
【问题讨论】:
您的示例中有错字,这可能是问题的一部分。您在.first
/.last
调用之前缺少右括号。应该是这样的:Bucket.where(issued_date: nil, bucket_type: 'PR').first
@Andrew 很好,当我输入问题时,这只是一个错字。
【参考方案1】:
我不能真正告诉你第一个问题的答案,但是对于第二个问题,
好吧,你应该使用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 排序(好吧 rails 不是 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 不对用户进行排序,这也更快。
附:这可能会根据使用的数据库而改变,但limit
无论如何都是要走的路。但是只要我们不知道使用的数据库,您的问题 1 就无法解决。此外,这应该分为两个问题
【讨论】:
感谢您的回复。Bucket.where(issued_date: nil, bucket_type: 'PR'.first
等价于SELECT "buckets".* FROM "buckets" WHERE "buckets"."bucket_type" = $1 AND "buckets"."issued_date" IS NULL ORDER BY "buckets"."id" ASC LIMIT 1 [["bucket_type", "PR"]]
和.last
相同,订单将变为DESC
。
我知道,看看我的答案第一个代码块的 mariaDB 部分。 limit
将删除“无用”ORDER BY
。
令人惊讶的是,当我删除 ORDER BY 时,查询时间更长 :( 因为我确实尝试过SELECT buckets.* FROM buckets WHERE issued_date IS NULL and bucket_type = 'PR' LIMIT 1
。这个SELECT buckets.* FROM buckets WHERE issued_date IS NULL and bucket_type = 'PR' ORDER BY id DESC LIMIT 1
仍然是赢家以上是关于Rails ActiveRecord where 子句与 .first 与 .last 在本地与生产的主要内容,如果未能解决你的问题,请参考以下文章
Rails ActiveRecord where 子句与 .first 与 .last 在本地与生产
如何将此查询转换为ActiveRecord(Rails 5)
Rails/ActiveRecord order by Array