处理两个几乎相同的表作为一个模型

时间:2013-06-08 18:58:53

标签: mysql ruby-on-rails-3 activerecord

我正在为维基媒体实验室的维基媒体数据库创建一些模型,由于技术问题,有两个表用于修订。

一个表revision包含所有修订版,但缺少用户名索引,因为它是由表达式组成的视图,用于某些修订版的空白用户名。

另一个表revision_userindex具有索引,但缺少受消隐影响的修订版。

除此之外,它们是相同的,我希望能够将它用作单个模型,而不是将索引的低级细节传递给用户。

目前我有以下代码:

class Revision < ActiveRecord::Base
  self.table_name = :revision_userindex # or :revision
  self.primary_key = :rev_id

  has_many :externallinks, :class_name => 'Externallink', :foreign_key => :el_from    
  has_many :iwlinks, :class_name => 'Iwlink', :foreign_key => :iwl_from    
  has_many :langlinks, :class_name => 'Langlink', :foreign_key => :ll_from    
  has_many :pagelinks, :class_name => 'Pagelink', :foreign_key => :pl_from    
  has_many :recentchanges, :class_name => 'Recentchange', :foreign_key => :rc_this_oldid    
  belongs_to :page, :class_name => 'Page', :foreign_key => :rev_page    
  belongs_to :user, :class_name => 'User', :foreign_key => :rev_user    
  has_many :templatelinks, :class_name => 'Templatelink', :foreign_key => :tl_from    
  has_many :texts, :class_name => 'Text', :foreign_key => :old_id    
end

但我不知道动态设置self.table_name的任何方法取决于在上下文中何时指定用户。即。

Revision.find_by_page("page")
Revision.find(nnn)
Revision.where(...) # where the query doesn't link to the user table
@page.revisions
# etc...

应使用revision表和

Revision.find_by_user("user")
@user.revisions

应该使用revision_userindex

如果可以在没有重新实现一半AR的情况下完成,我很乐意知道。

两个表的primary_key相同。

供参考,以下是表格的定义:

首先进行修订:

CREATE ALGORITHM=UNDEFINED DEFINER=`viewmaster`@`%` SQL SECURITY DEFINER VIEW `revision` AS
SELECT `enwiki`.`revision`.`rev_id` AS `rev_id`,
       `enwiki`.`revision`.`rev_page` AS `rev_page`,
       if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_text_id`) AS `rev_text_id`,
       if((`enwiki`.`revision`.`rev_deleted` & 2),NULL,`enwiki`.`revision`.`rev_comment`) AS `rev_comment`,
       if((`enwiki`.`revision`.`rev_deleted` & 4),NULL,`enwiki`.`revision`.`rev_user`) AS `rev_user`,
       if((`enwiki`.`revision`.`rev_deleted` & 4),NULL,`enwiki`.`revision`.`rev_user_text`) AS `rev_user_text`,
       `enwiki`.`revision`.`rev_timestamp` AS `rev_timestamp`,
       `enwiki`.`revision`.`rev_minor_edit` AS `rev_minor_edit`,
       `enwiki`.`revision`.`rev_deleted` AS `rev_deleted`,
       if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_len`) AS `rev_len`,
       `enwiki`.`revision`.`rev_parent_id` AS `rev_parent_id`,
       if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_sha1`) AS `rev_sha1`
FROM `enwiki`.`revision`

对于revision_userindex:

CREATE ALGORITHM=UNDEFINED DEFINER=`viewmaster`@`%` SQL SECURITY DEFINER VIEW `revision_userindex` AS
SELECT `enwiki`.`revision`.`rev_id` AS `rev_id`,
       `enwiki`.`revision`.`rev_page` AS `rev_page`,
       if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_text_id`) AS `rev_text_id`,
       if((`enwiki`.`revision`.`rev_deleted` & 2),NULL,`enwiki`.`revision`.`rev_comment`) AS `rev_comment`,
       `enwiki`.`revision`.`rev_user` AS `rev_user`,
       `enwiki`.`revision`.`rev_user_text` AS `rev_user_text`,
       `enwiki`.`revision`.`rev_timestamp` AS `rev_timestamp`,
       `enwiki`.`revision`.`rev_minor_edit` AS `rev_minor_edit`,
       `enwiki`.`revision`.`rev_deleted` AS `rev_deleted`,
       if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_len`) AS `rev_len`,
       `enwiki`.`revision`.`rev_parent_id` AS `rev_parent_id`,
       if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_sha1`) AS `rev_sha1`
FROM `enwiki`.`revision`
WHERE ((`enwiki`.`revision`.`rev_deleted` & 4) = 0)

3 个答案:

答案 0 :(得分:0)

只需将常用逻辑/配置放入mixin中,然后使用包含它的两个不同模型。这样的事情(未经测试,但你明白了):

module RevisionCommon
 extend ActiveSupport::Concern

 included do

  has_many :externallinks, :class_name => 'Externallink', :foreign_key => :el_from    
  has_many :iwlinks, :class_name => 'Iwlink', :foreign_key => :iwl_from    
  has_many :langlinks, :class_name => 'Langlink', :foreign_key => :ll_from    
  has_many :pagelinks, :class_name => 'Pagelink', :foreign_key => :pl_from    
  has_many :recentchanges, :class_name => 'Recentchange', :foreign_key => :rc_this_oldid    
  belongs_to :page, :class_name => 'Page', :foreign_key => :rev_page    
  belongs_to :user, :class_name => 'User', :foreign_key => :rev_user    
  has_many :templatelinks, :class_name => 'Templatelink', :foreign_key => :tl_from    
  has_many :texts, :class_name => 'Text', :foreign_key => :old_id
 end

 module ClassMethods
  # if any
 end
end

class RevisionForPage < ActiveRecord::Base
  self.table_name = :revision_userindex # or :revision
  self.primary_key = :rev_id

  include RevisionCommon
end

class RevisionForUser < ActiveRecord::Base
  self.table_name = :revision_userindex # or :revision
  self.primary_key = :rev_user_id # or whatever
  include RevisionCommon
end

答案 1 :(得分:0)

revisionrevision_userindex是视图,而不是表格。只需将您的模型直接链接到enwiki.revision表。

未经授权的直接访问

[编辑]

由于您无权访问基础物理表,因此我建议在这两个视图之上创建聚合视图,并将模型链接到此新视图。 MySQL优化器应该能够决定使用哪个版本的视图。

CREATE ALGORITHM=MERGE VIEW revision_superview AS
SELECT
    r.rev_id, r.rev_page,
    COALESCE(r.rev_user, ru.rev_user) AS rev_user,
    -- and so on
FROM revision AS r JOIN revision_user AS ru USING (rev_id)

<击>

视图上的

查询不使用索引

[编辑]

已确认no index will be used具有上述解决方法。

也许您最好的选择是让您的DBA创建另一个直接从物理表聚合数据的视图。告诉他/她您已经可以访问这些数据。如果他/她需要,我很乐意写出视图定义。

答案 2 :(得分:0)

你有两个不同的表,“计算机”应该选择在某一点使用哪一个。有3个地方可以发生:

  1. 您的代码:您创建2个AR,并且假设IF语句选择使用哪个AR。

  2. SQL代码:SP或视图中的IF语句。但是在MySQL中是不可能的(据我所知)。 - 如果可能的话,这将是我的首选方案。

  3. SQL语句,然后查询优化器将选择正确的表。有几种方法可以选择进入SQL语句。可以通过JOINUNION ALL或其他方式完成。但是在我看来,MySQL引擎不是为这类东西设计的。它的专长是快速处理行。进行比较和添加抽象层是插件,使用有限。

  4. 我建议创建2个AR对象并选择其中一个。您可以在不指定表名的情况下创建AR类,然后2个新的AR对象将继承所有属性,并将定义表名。我对Ruby的了解有限,所以如果这个建议不完全正确,我会道歉。