<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>391117134 (Zkuns)</title>
    <link>https://ruby-china.org/391117134</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Rails 的 includes 预加载的实际调用</title>
      <description>&lt;p&gt;前些天因为想减少站点的 sql 查询，过程中遇到些问题，搜解决办法时看到一篇文章，受益匪浅，想把自己的看完的理解总结然后分享一下，如果有不对的地方，求指导。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.arkency.com/2013/12/rails4-preloading" rel="nofollow" target="_blank" title=""&gt;原文章地址请点在这里&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="三种预加载方式"&gt;三种预加载方式&lt;/h2&gt;
&lt;p&gt;我们在 rails 中有三种方式可以用来预加载数据，分别是 eager_load preload includes. 但是实际上预加载的方式就只有两种，preload 和 eager_load, 而 includes 是做什么的呢？它是内部自行判断，然后调用 preload 或是 eager_load。&lt;/p&gt;
&lt;h2 id="假如"&gt;假如&lt;/h2&gt;
&lt;p&gt;有个 User(用户) model 还有一个 Post(帖子) model，它们是一对多的关系&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先先看看使用 preload 和 eager_load 这两中加载方法有什么区别&lt;/p&gt;
&lt;h2 id="preload"&gt;preload&lt;/h2&gt;
&lt;p&gt;使用 preload 将查出用户的时候预加载出用户发的帖子&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#select “users”.* from “users"&lt;/span&gt;
&lt;span class="c1"&gt;#select “posts”.* from “posts” where “posts”. “user_id” IN (1, 2, 3)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它生成两句 sql 语句，第二句预加载了 posts 表的内容。但是这种方式有个弊端，当你在预加载的时候想给预加载的表加上条件的时候就有问题了，比如我想加载用户的同时加载这个用户点赞高于 100 的例子&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.like_count &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#报错&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 preload 这种加载方式是使用两句查询语句，并没有 join posts 表，所以不能这样给 post 加上条件&lt;/p&gt;

&lt;p&gt;想要给预加载的表加上条件，那就试试使用 eager_load 的方式来预加载&lt;/p&gt;
&lt;h2 id="eager_load"&gt;eager_load&lt;/h2&gt;
&lt;p&gt;因为 sql 太长所以分了几行&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eager_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#select "users"."id" AS t0_r0, "users"."name" AS t0_r1, "users"."email" AS t0_r2,&lt;/span&gt;

&lt;span class="c1"&gt;#"posts"."id" AS t1_r0, "posts"."title" AS t1_r1,"posts"."like_count" AS t1_r2, "posts"."users_id" AS t1_r3&lt;/span&gt;

&lt;span class="c1"&gt;#from "users" left outer join "posts" on "posts"."users_id" = "users"."id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到最后一行 eager_load 预加载数据是将 users 和 posts 做了左连接，所以 eager_load 是可以给 post 加上条件查询语句的，即：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eager_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.like_count &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成的 sql 语句为，前面两行相等，最后一行多了个查询条件：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="n"&gt;outer&lt;/span&gt; &lt;span class="n"&gt;join&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"users_id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;like_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看，条件查询就可以正常使用了。其实 preload 也是可以实现条件加载，下面会说到。&lt;/p&gt;
&lt;h2 id="includes"&gt;includes&lt;/h2&gt;
&lt;p&gt;最后说一下 inludes, 在 Rails3 中 includes 它是可以自行判断这种一对多的预加载条件查询的，即当你使用&lt;/p&gt;

&lt;p&gt;User.includes('posts')
它会调用
User.preload('posts')
当使用
User.includes('posts').where("posts.like_count &amp;gt; ?", 100)
它会自行调用
User.eager_load('posts').where("posts.like_count &amp;gt; ?", 100)&lt;/p&gt;

&lt;p&gt;但是在 Rails4 中使用，就会报错，报错信息里面有着详细解释，截取一段&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.like_count &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#DEPRECATION WARNING: It looks like you are eager loading table(s)&lt;/span&gt;
&lt;span class="c1"&gt;# (one of: users, addresses) that are referenced in a string SQL&lt;/span&gt;
&lt;span class="c1"&gt;# snippet. For example:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#    Post.includes(:comments).where("comments.title = 'foo'")&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Currently, Active Record recognizes the table in the string, and knows&lt;/span&gt;
&lt;span class="c1"&gt;# to JOIN the comments table to the query, rather than loading comments&lt;/span&gt;
&lt;span class="c1"&gt;# in a separate query. However, doing this without writing a full-blown&lt;/span&gt;
&lt;span class="c1"&gt;# SQL parser is inherently flawed. Since we don't want to write an SQL&lt;/span&gt;
&lt;span class="c1"&gt;# parser, we are removing this functionality. From now on, you must explicitly&lt;/span&gt;
&lt;span class="c1"&gt;# tell Active Record when you are referencing a table from a string&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#   Post.includes(:comments).where("comments.title = 'foo'").references(:comments)&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原来这种自行判断的方法在 Rails4 时被去掉了，在预加载对象时使用判断语句，它还是会使用 preload。&lt;/p&gt;

&lt;p&gt;有两个办法解决：&lt;/p&gt;

&lt;p&gt;一个是直接调用 eager_load&lt;/p&gt;

&lt;p&gt;另一个是像上面报错信息里面举得例子一样使用 references&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.like_count &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#或是&lt;/span&gt;
&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.like_count &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个方法生成的 sql 语句都是一样的，都是左连接查询&lt;/p&gt;
&lt;h2 id="结合使用association来实现预加载"&gt;结合使用 association 来实现预加载&lt;/h2&gt;
&lt;p&gt;其实 preload 或是 eager_load 还可以通过 association 实现预加载&lt;/p&gt;

&lt;p&gt;例如：
在 user 的 model 中加入&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:popular_posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="s2"&gt;"like_count &amp;gt; 100"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;class_name: &lt;/span&gt;&lt;span class="s1"&gt;'Post'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 preload 来预加载会生成两条 sql 语句来查询&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:popular_posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#生成的sql语句&lt;/span&gt;
&lt;span class="c1"&gt;#select "users".* from "users"&lt;/span&gt;
&lt;span class="c1"&gt;#select "posts".* from "posts" where "posts"."like_count" &amp;gt; 100 AND "posts"."user_id" in (1, 2, 3)&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 eager_load 效果&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eager_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:popular_posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#生成sql语句和下面一样，都是左链接&lt;/span&gt;
&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eager_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.like_count &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>391117134</author>
      <pubDate>Mon, 17 Aug 2015 21:58:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/26962</link>
      <guid>https://ruby-china.org/topics/26962</guid>
    </item>
    <item>
      <title>【新手】请大家帮我看看，用 ajax 传出 json 问题</title>
      <description>&lt;p&gt;我向服务器请求 json，服务器接收到后应该返回 json，但是 js 中的回调函数就不执行，如果我将 js 中的{name:"John"}去掉就能执行，实在是不知道为什么这样。
controller 的代码：
    def get
        name = params[:name]
        respond_to do |format|
          format.json  {render json:name}
        end
    end&lt;/p&gt;

&lt;p&gt;js 代码：
    $.getJSON('/tests/get',{ name: "John" },function(data){
      alert("success")
      $("#label").attr('style','visibility:visible')
    }，‘json’);&lt;/p&gt;

&lt;p&gt;服务器端也没报错，js 代码也没报错。&lt;/p&gt;</description>
      <author>391117134</author>
      <pubDate>Wed, 22 Oct 2014 11:13:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/22182</link>
      <guid>https://ruby-china.org/topics/22182</guid>
    </item>
  </channel>
</rss>
