新手问题 大家看下这段`ActiveRecord 下 find 方法实现' 的伪码是不是有问题?

zw963 · 2012年08月01日 · 最后由 fsword 回复于 2012年08月03日 · 3014 次阅读
  def self.find(selector, hash={})

    hash[:join] ||= ""
    hash[:conditions] ||= ""

    sql = ["SELECT * FROM books"]
    hash[:joins].each do |join_table|
      sql << "LEFT OUTER JOIN #{join_table} ON"
      sql << "self.#{join_table.to_s.chop}_id"
      sql << "= #{join_table}.id"
    end
    sql << "where #{hash[:conditions]}" unless hash[:conditions].empty?
    sql << "LIMIT 1" if selector == :first
  end
  connection.find(sql.join(""))
end

这段代码关键是下面这一句:

sql << "LEFT OUTER JOIN #{join_table} ON"

上面这行代码是不是写错了。joins貌似应该是调用的 SQL 的内连接才对呀?而这里却使用了 SQL 的左外连接,

我觉得是不是应该改为:

sql << "INNER JOIN #{join_table} ON"

才符合joins的真实含意?

怎么没人回复呢??

sql << "whre #{hash[:conditions]}" unless hash[:conditions].empty? 至少我知道这句是错了。。

#2 楼 @jjym

很明显是我打错了...

不知道这是哪里的源码。根据上下文来看 book belongs_to join_table(注意 id) 所以这样查和内连接效果是差不多的。 不过是否有效率等等的高深目的就不知道了

不知道这个是哪里的代码,但我想这个只是 SQL 的写法问题,如果写成内连接 INNER JOIN 的话,一旦 join_table 里面没有#{join_table}.id的话,整个返回 SQL 数据就空了,可能这样的行为不是这个方法期望的,所以这里写成 LEFT OUTER JOIN,即使 join_table 里面没有 id 的记录,也不影响整个 find 的方法按照#{hash[:conditions]返回符合hash[:conditions]的记录。 不过这里可能有另外一个 bug,hash[:conditions]有多个 key, value 的话,貌似拼接出来的 SQL 应该不对才对。

#4 楼 @jjym #5 楼 @ericguo

代码其实有出处,来自于重构 Ruby 版。

@jjym, 你说的没错,所以我推论出了一个约定, 那就是: joins 只有在 声明 belongs_to 所在的表内使用才有意义 这个说法到底对不对?至少在这段代码来看是没错的。我想大多数人都忽视了这一点。

@ericguo , 你说的不存在 id 的情况,根本就没办法使用 joins. 所以根本就不用考虑。完整的 find 实现肯定会对这个情况有一个异常处理。现在的问题是:到底应该是内连接还是外连接?这是两个完全不同的概念。你怎么会说所以这里写成..., 这个说法是错误的。也就是在这个地方,我觉得这代码是错的。

  • 首先,这个源代码应该和 Rails 的代码无关吧。应该是特定的应用里面的代码,所以对belongs_to的定义应该不会有影响。只是对books有影响吧
  • 其次,同意 @ericguo 的说法。这个方法的意思应该是重新定义 Books 的 Find 方法。目的应该是从 Books 表里查找记录,同时去各个关联表里找到相关的信息。如果相关表里没有信息,那对应的字段就为空。

#6 楼 @zw963 我觉得你的问题很奇怪啊,是否用外连接要看场景,你这里的查询目的是什么?

#7 楼 @blueplanet #8 楼 @fsword

嗨,谢谢你们的回复。

好吧,我承认,我再次犯了一个错误,就是问问题的时候,太随意了。没有表达清楚我的真实意图。

我已经重新修改了标题以及内容, 你们再看下,帮忙看看我的理解对不对。

#9 楼 @zw963 这是作者需要的 find 和 joins

你所谓的真实含义是字面上和 Rails 方法中的 joins

#9 楼 @zw963 这个应该是符合 find 定义的,看 api 文档

:joins - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)

说白了,我没看明白啥问题。

#11 楼 @fsword

嗨~ 我自己通过 google 已经查证了。

User.find(:all, :limit => 10, :joins => :user_points,
                :select => "users.*, count(user_points.id)", :group =>
                "user_points.user_id")
which generates following sql

会生成以下 SQL 语句:

SELECT users.*, count(user_points.id) 
FROM `users` 
INNER JOIN `user_points` 
ON user_points.user_id = users.id 
GROUP BY user_points.user_id 
LIMIT 10

这证明我的推断没错,joins 用的就是内连接。

至于你贴给我的那个链接,你完全被内容被误导了。

链接如下:http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html

:joins - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed). The records will be returned read-only since they will have attributes that do not correspond to the table's columns.

这句英文的真实含义是要告诉你:你可以通过给 joins 传入一个 SQL 字符串,来实现左连接 (也就是左外连接) 的效果。就像帮助里讲的那样,LEFT JOIN comments ON comments.post_id = id" 如果你直接使用 .joins,那还是内连接。

所以,我这个帖子内的那个 find 实现,很明显是错滴,它把inner join 写成了 `left outer join了。

看来以后问问题之前,还是应该多做功课,我发现我特别不善于表达自己的问题,相比较而言,回答问题甚至更靠谱点...

#13 楼 @zw963 恩,你说的对,我是没看懂

:joins - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed), named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s)
需要 登录 后方可回复, 如果你还没有账号请 注册新账号