[关闭]
@elibinary 2017-04-07T04:19:54.000000Z 字数 3143 阅读 679

ActiveRecord::Relation Methods 总结

Rails


总结几个功能性很强很好用的方法

find_each

有时会有一些需求去遍历处理大批量的数据,比如要写个脚本去遍历处理一张表的数据,这个时候就可以使用 find_each 方法了。

  1. # find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
  2. # The find_each method uses find_in_batches with a batch size of 1000 (or as specified by the :batch_size option).

相比于使用 each 的优势就是,each 方法会一次性取出所有数据放到内存里再进行遍历,而 find_each 方法会分批次读取,一次读取默认值为 1000,当待处理的数据量大的时候就会显得非常有用。

Options

to_sql

  1. # /active_record/relation.rb
  2. # to_sql()
  3. # Returns sql statement for the relation.

有时你用 ActiveRecord 写了一个稍复杂的数据库操作,不确定它到底会不会按构想中生成 sql ,这时就可以使用 to_sql 方法把生成的 sql 打印出来看一看了。

  1. User.joins(:talks).to_sql
  2. #=> "SELECT `users`.* FROM `users` INNER JOIN `user_talks` ON `user_talks`.`user_id` = `users`.`id` INNER JOIN `talks` ON `talks`.`id` = `user_talks`.`talk_id`"

explain

是不是异常熟悉,没错在 ActiveRecord::Relation 中也可以使用 explain 来输出 sql 的分析结果,其用途与数据库中一般无二。

  1. User.where(nickname: 'xxx').explain
  2. # => EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`nickname` = 'xxx'
  3. # +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
  4. # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  5. # +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
  6. # | 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 121 | Using where |
  7. # +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
  8. # 1 row in set (0.00 sec)

其源码也很简单

  1. # File activerecord/lib/active_record/relation.rb
  2. def explain
  3. #TODO: Fix for binds.
  4. exec_explain(collecting_queries_for_explain { exec_queries })
  5. end
  1. # File activerecord/lib/active_record/explain.rb
  2. # Executes the block with the collect flag enabled. Queries are collected
  3. # asynchronously by the subscriber and returned.
  4. def collecting_queries_for_explain # :nodoc:
  5. ExplainRegistry.collect = true
  6. yield
  7. ExplainRegistry.queries
  8. ensure
  9. ExplainRegistry.reset
  10. end
  11. # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
  12. # Returns a formatted string ready to be logged.
  13. def exec_explain(queries) # :nodoc:
  14. str = queries.map do |sql, bind|
  15. [].tap do |msg|
  16. msg << "EXPLAIN for: #{sql}"
  17. unless bind.empty?
  18. bind_msg = bind.map {|col, val| [col.name, val]}.inspect
  19. msg.last << " #{bind_msg}"
  20. end
  21. msg << connection.explain(sql, bind)
  22. end.join("\n")
  23. end.join("\n")
  24. # Overriding inspect to be more human readable, especially in the console.
  25. def str.inspect
  26. self
  27. end
  28. str
  29. end

可以看到,最后是使用 db 对应的 adapter 去执行 explain 命令然后把结果打印

merge

这个方法真的是黑科技,从发现到现在我已经无法离开这个魔术方法了,先看下它的文档描述

Merges in the conditions from other, if other is an ActiveRecord::Relation. Returns an array representing the intersection of the resulting records with other, if other is an array.

  1. Weixin::User.where(nickname: 'xxx').merge(Weixin::User.joins(:tags).where(weixin_tags: {id: 1})).to_sql
  2. #=> "SELECT `weixin_users`.* FROM `weixin_users` INNER JOIN `weixin_user_tags` ON `weixin_user_tags`.`user_id` = `weixin_users`.`id` INNER JOIN `weixin_tags` ON `weixin_tags`.`id` = `weixin_user_tags`.`tag_id` WHERE `weixin_users`.`nickname` = 'xxx' AND `weixin_tags`.`id` = 1"

尤其当你要写 join 语句的时候它将非常好用,像这样

  1. Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注