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.lastwhere 子句之后的性能在本地和生产中不同? 有没有比上述查询更好的方法来获得一条记录,其中 bucket_type 等于 PR 并且 issue_date 为零 无需添加任何索引bucket_typeissued_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 查询不等于

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

如何将此查询转换为ActiveRecord(Rails 5)

Rails/ActiveRecord order by Array

Rails ActiveRecord - 获取与锁定的belongs_to关联

使用 ActiveRecord 语法从 Rails 中的连接表中选择或按列排序