0

有没有办法使用 ActiveRecord::Relation 为左外连接指定多个条件?

以下面的 SQL 语句为例。谁能用 ActiveRecord::Relation 对象重写它?

SELECT `texts`.*, `text_translations`.translation FROM `texts` LEFT OUTER JOIN `text_translations` ON `text_translations`.`id` = `texts`.`id` AND `text_translations`.`locale` = 'en'

在 ActiveRecord 3.0.3+ 下有没有办法做到这一点?

提前致谢。

4

1 回答 1

1

首先,您应该考虑使用 rails/activerecord 符合关系。这意味着 text_translations 表中的外键应称为 text_id

像这样创建模型和关联:

class Text < ActiveRecord::Base

  # all possible translations!
  has_many :text_translations

  scope :with_translation_for, lambda { |lang| {
    :select     => "texts.*, tt.translation",
    :joins      => "LEFT OUTER JOIN text_translations AS tt ON tt.text_id = texts.id AND tt.locale = #{ActiveRecord::Base.sanitize(lang)}"
  }}


  # return nil if translation hasn't been loaded, otherwise you get a nasty NoMethod exception
  def translation
    read_attribute(:translation)
  end

end

class TextTranslation < ActiveRecord::Base
  # every translation belongs to a text
  belongs_to :text

  # define a scope for the language
  scope :language, lambda { |lang| where(['locale = ?', lang]) }

end

如何使用:

texts = Text.with_translation_for('en')
texts.each do |c_text|
    unless c_text.translation.nil?
        puts c_text.translation
    else
        puts "No translation available!"
    end
end

现在来看看利弊,即使没有所需语言的文本翻译,使用 LEFT OUTER 连接的方式也会加载所有文本。缺点是您不会获得“TextTranslation”模型对象。

另一种方法是仅加载具有所需翻译的文本。你可以这样做:

texts = Text.includes(:text_translations).where(:text_translations => {:locale => 'en'})

现在texts[i].text_translations将返回一个数组,其中包含与区域设置“en”匹配的此文本的所有 TextTranslations 模型对象。但是在区域设置“en”中没有翻译的文本不会显示。

编辑

连接到您的评论:

在关系上使用的问题.join(:tablename)是,它会导致 INNER JOIN,所以这不是一个选项。您必须明确声明左连接。另一件事是,如果您使用类似Text.includes(:text_translations).where(['text_translations.locale = ?', 'en'])条件的东西,则将整体应用于 SQL 查询,而不是可能的 LEFT 连接本身。您实际上可以做的是声明关联,例如

has_many :english_translations, :class_name => 'TextTranslation', :conditions => ['locale = ?', 'en']  

这样,您可以通过急切加载(根本没有任何连接)来管理仅加载英文翻译:

Text.includes(:english_translations).all

检查一下:

于 2011-03-05T16:50:53.140 回答