Rails [已解决]MySQL 多对多查询的问题 (数组 and 查询)

colorfulberry · 2015年10月29日 · 最后由 jiemoon 回复于 2015年11月01日 · 6620 次阅读

数据关系如下

class blogs
  has_and_belongs_to_many :categories
end

class categories
  has_and_belongs_to_many :blogs
end

问题如下

  • 现在想查询出 category_ids 包含 [2,3] 的 公开 博客
  • 自己尝试着用 ransack 的_eq_all,结果并不理想,_in 的话查出了所有 id 为 2 或者 3 的博客,
SELECT COUNT(DISTINCT `blogs`.`id`) FROM `blogs` LEFT OUTER JOIN `blogs_categories` ON `blogs_categories`.`blog_id` = `blogs`.`id` WHERE `blogs`.`status` = 2 AND (`blogs_categories`.`category_id` = 3 AND `blogs_categories`.`category_id` = 2)

@rei @lgn21st @vkill

为什么不是where blogs_categories.category_id in (2,3) and blogs.status = 2? 就算是要同时满足 (2,3) 的话也可以直接在关系表上查啊,没必要连接 categories 表吧……

@msg7086 in 的话查处来的关系时or的关系,不是and, 的确没必要连 categories 表,当时写那个表也是因为懒

@vikill 感谢,采用了 arel-helpers 的 QueryBuilder

我最近也是碰到这个问题,我的方法是:

-- 把 含有 [2,3] 分类的  blog_id 查询出来
select blog_id, count(1) c from blogs_categories where category_id in (2,3)  group by blog_id having c = 2

😄

@jiemoon 你的确查出了 2 和 3 的部分博客,但是如果 category_id 有 2 和 3 和 4 的博客就查不出来 ~~我觉得应该这样 select blog_id, count(1) c from blogs_categories where category_id in (2,3) group by blog_id having c >= 2

#5 楼 @colorfulberry 直接 = 是可以的,因为 category_id in (2,3) 这个条件会把含有 2 or 3 的 category_id 的博客查询出来(也包括 category_id234 的博客)

@jiemoon 目前我的采纳的做法是用了 querybuilder

q = query
category_ids.each do |category_id|
  blog_category_table_alias = BlogCategory.arel_table.alias("%s_%s" % [BlogCategory.table_name, category_id])

  q = q.joins(
    Blog.arel_table.join(blog_category_table_alias, Arel::Nodes::InnerJoin)
      .on(
        Blog[:id].eq(blog_category_table_alias[:blog_id]).and(
          blog_category_table_alias[:category_id].eq(category_id)
        )
      )
      .join_sources
  )
end

对应的 sql

SELECT `blogs`.* FROM `blogs` INNER JOIN `blog_categories` `blog_categories_2` ON `blogs`.`id` = `blog_categories_2`.`blog_id` AND `blog_categories_2`.`category_id` = 2 INNER JOIN `blog_categories` `blog_categories_3` ON `blogs`.`id` = `blog_categories_3`.`blog_id` AND `blog_categories_3`.`category_id` = 3 WHERE `blogs`.`status` = 2

@colorfulberry

这是我的表数据

查询 tag_id 有 1,2 的 video

我是这样试的,或许我们的表结构不一样 😪

querybuilder 看得有点复杂,我研究看看 😃

#7 楼 @colorfulberry 我好奇 querybuilder 最后拼出来的 SQL 语句是什么样子的,能发来看看?

@jiemoon

sql 时根据 category_id 动态生成的 如下:

 pry(main)> BlogQueryBuilder.new.for_xxx([2,3]).to_sql
=> "SELECT `blogs`.* FROM `blogs` INNER JOIN `blog_categories` `blog_categories_2` ON `blogs`.`id` = `blog_categories_2`.`blog_id` AND `blog_categories_2`.`category_id` = 2 INNER JOIN `blog_categories` `blog_categories_3` ON `blogs`.`id` = `blog_categories_3`.`blog_id` AND `blog_categories_3`.`category_id` = 3 WHERE `blogs`.`status` = 2"
[6] pry(main)> BlogQueryBuilder.new.for_xxx([1,2,3,4]).to_sql
=> "SELECT `blogs`.* FROM `blogs` INNER JOIN `blog_categories` `blog_categories_1` ON `blogs`.`id` = `blog_categories_1`.`blog_id` AND `blog_categories_1`.`category_id` = 1 INNER JOIN `blog_categories` `blog_categories_2` ON `blogs`.`id` = `blog_categories_2`.`blog_id` AND `blog_categories_2`.`category_id` = 2 INNER JOIN `blog_categories` `blog_categories_3` ON `blogs`.`id` = `blog_categories_3`.`blog_id` AND `blog_categories_3`.`category_id` = 3 INNER JOIN `blog_categories` `blog_categories_4` ON `blogs`.`id` = `blog_categories_4`.`blog_id` AND `blog_categories_4`.`category_id` = 4 WHERE `blogs`.`status` = 2"

@jiemoon 没你这一条简单!唉昨天是因为数据生成出了错误,重复存了一些 category_id。所以导致没有查正确

#11 楼 @colorfulberry 好复杂的 sql 😃 , 学习了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号