合并尝试将表连接到自身

时间:2018-01-05 01:47:48

标签: sql ruby-on-rails

我有一个故事模型和一个标签模型,彼此有_and_belongs_to_many。标签有一个名为adult的字段,表示任何包含该标签的故事都包含成人内容。 Story有一个名为adult_override的字段,表示即使其标记都不是成人,它也有成人内容。我有以下函数,它使用一个ActiveRecord :: Relation代表一组故事并过滤它只包含成人故事(这是Story类的一个方法):

def self.only_adult(story_set)
  story_set.left_outer_joins(:tags)
           .distinct
           .where("stories.adult_override = true OR tags.adult = true")
end

我还有一个函数应该采用一组给定的故事(一个ActiveRecord :: Relation)并找到该集合中最常见的标签(这是Tag类的一个方法):

def self.most_common(story_set, num = 10)
  joins(:stories).merge(story_set)
                 .select("tags.*, COUNT(*) AS cnt")
                 .group("tags.name")
                 .reorder("cnt DESC")
                 .limit(num)
end

(name是tags表的主键)

这些方法都可以自行运行,但是当我尝试在已经被only_adult过滤的关系上调用most_common时,我收到以下错误:

  

无法将“标记”加入名为“标记”的关联中;也许你拼错了   它?

即使在要传递的story_set中已经引用了tags表,我如何让most_common工作?

编辑:Tag.joins(故事)生成的SQL是:

  

SELECT“tags”。* FROM“tags”INNER JOIN“stories_tags”ON   “stories_tags”。“name”=“tags”。“name”INNER JOIN“stories”ON   “故事”。“id”=“stories_tags”。“story_id”

和only_adult生成的SQL在一个例子中是:

  

SELECT DISTINCT“故事”。* FROM“故事”LEFT OUTER JOIN   “stories_tags”ON“stories_tags”。“story_id”=“故事”。“id”LEFT   OUTER JOIN“tags”ON“tags”。“name”=“stories_tags”。“name”WHERE   (true)AND(EXISTS(SELECT 1 FROM characters_stories INNER JOIN   字符ON characters_stories.character_id = characters.id WHERE   (characters_stories.story_id = stories.id)AND(LOWER(characters.name)   类似'doc%')))AND(stories.adult_override = true OR tags.adult   = true)订购“故事”。“作者”ASC

合并这两个关系时会出现问题。

1 个答案:

答案 0 :(得分:0)

好的,我想出了如何正常工作,所以我会添加我的解决方案以防其他人遇到类似的问题。我替换了most_common的主体(为了简单起见,编辑了一下):

  rflct = reflect_on_association(:stories)
  join_table = rflct.join_table
  join_sql = "INNER JOIN #{join_table} AS jt ON " +
             "jt.#{rflct.foreign_key} = " +
             "answer.name}"
  processed_set = story_set.where("stories.id = jt.#{rflct.association_foreign_key}")

  self.select('answer.*, COUNT(*) as cnt')
      .from("tags AS answer")
      .joins(join_sql)
      .where("EXISTS (#{processed_set.to_sql})")
      .group("answer.tags")
      .reorder('cnt DESC')
      .limit(num)

我对此并不完全满意 - 我觉得这可能是一种更优雅的方式来实现这一目标 - 但它确实有效。