2010-11-23 78 views
0

我正在改写几天前提出的一个问题。 click this看到上一个问题Rails 2.3.8:如何根据字段的最高值选择记录?

我已经回答了这个问题,但它是不正确的。

表中的记录

alt text

Revision.find(:all,:select => "id,name,max(revision) as revision", :conditions => ["saved=1"],:group => 'name') 

这将导致

alt text

其实结果应该是ID 3,6,8。

查询中的哪些修改将导致此结果?

回答

1

这是SQL不太适合的查询类型。简而言之,问题在于,您实际上需要的是组操作,然后在每个组内排序,然后从每个子组中获取最高记录。事实证明,这是一个令人惊讶的难题,因为SQL在排序之前会进行分组。通常,分组是针对汇总数据的,而不是选择特定的记录。

有很多SQL特定的方法可以解决这个问题,但它们都不适合Rails。相反,我会组织你的数据,像这样:

  1. 拥有recipe_revisions模型,其中包括一个完整的配方记录
  2. 有一个配方模型,这基本上只是ID /名称/最新版本。
  3. 要获得配方的当前副本,您需要从配方中选择所需的配方,然后加入名称/最大修订版本的recipe_revisions,其中这些列在两个表上都编入索引。

这不是容易解决方案,它会运作良好。

0

将查询拆分为两部分。在第一个找到每个名称的最大修订版。在第二个查询中查找与名称和修订相匹配的完整行。

CREATE TEMP TABLE max_revisions (name varchar, revision integer); 
INSERT INTO max_revisions SELECT name, max(revision) FROM revisions 
     WHERE saved = 1 GROUP BY name; 
SELECT r.* FROM revisions r INNER JOIN max_revisions m 
     ON m.name = r.name AND m.revision = r.revision; 

现在你的问题可能是,如何在Rails中表达这一点。

您可以使用Revision.connection.execute然后使用Revision.find_by_sql(都包装在一个方法中,例如:Revision.find_by_max_revisions)。

否则,如果你的数据库不支持临时表,或者你只是不想让他们,你可以读取max_revisions到内存中,然后用它来构建查询:

class Revision 
    def self.find_by_max_revisions 
    max_revs = connection.select_values(
     sanitize_sql(
     ["SELECT name, max(revision) as max_rev FROM #{table_name} 
       WHERE saved = ? GROUP BY name", 1] 
    ), "Load max_revisions for #{self.name}" 
    ) 
    max_revs.map do |row| 
     find_by_name_and_revision(row["name"], row["max_rev"].to_i) 
    end 
    end 
end 
0
Revision.find(:all, :select => "DISTINCT id", :conditions => ["saved=1"], :order => 'revision DESC') 
0

我有类似的问题,我还没有能够将其转换为活动记录语法,但同事帮助我掀起了一个嵌套选择,为我解决了这个问题。事情是这样的:

select id, name, saved, revision 
from revisions r 
where saved = 1 and 
version_no = (select revision 
       from revisions r2 
       where saved = 1 and r2.name = r.name 
       order by r2.revision desc 
       limit 1 
      ) 

警告:我不知道的性能影响,虽然,我怀疑这是使第二子查询每一个独特的修订场。

相关问题