我正在为维基媒体实验室的维基媒体数据库创建一些模型,由于技术问题,有两个表用于修订。
一个表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)
答案 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)
revision
和revision_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个地方可以发生:
您的代码:您创建2个AR,并且假设IF语句选择使用哪个AR。
SQL代码:SP或视图中的IF语句。但是在MySQL中是不可能的(据我所知)。 - 如果可能的话,这将是我的首选方案。
SQL语句,然后查询优化器将选择正确的表。有几种方法可以选择进入SQL语句。可以通过JOIN
或UNION ALL
或其他方式完成。但是在我看来,MySQL引擎不是为这类东西设计的。它的专长是快速处理行。进行比较和添加抽象层是插件,使用有限。
我建议创建2个AR对象并选择其中一个。您可以在不指定表名的情况下创建AR类,然后2个新的AR对象将继承所有属性,并将定义表名。我对Ruby的了解有限,所以如果这个建议不完全正确,我会道歉。