Rails + MySQL,多对多关系的交集

时间:2017-04-29 08:32:14

标签: mysql ruby-on-rails

我有多对多的网站和代码,通过联接表SitesTags连接:

Site
  has_and_belogs_to_many :tags
  id name
  1  siteA
  2  siteB

Tag
  # has_and_belogs_to_many :sites
  id name
  1  tagA
  2  tagB
  3  tagC

SitesTags
  site_id tag_id
  1       1
  1       2
  2       2
  2       3

我想获得两个网站共有的COUNT个标签。 在此示例中,将有一个常用标记siteA和siteB(tagB)。

理想情况下,我想要数据库级别的解决方案,但我正在使用MySQL。 我试过(Site.find(1).tags & Site.find(2).tags).count 但是我可以看到这是在进行多次查询,并且它不是使用COUNT(*)而是获取所有数据:

Site Load (0.3ms)  SELECT  `sites`.* FROM `sites` WHERE `sites`.`id` = 1 LIMIT 1
Site Load (0.3ms)  SELECT  `sites`.* FROM `sites` WHERE `sites`.`id` = 2 LIMIT 1
Tag Load (0.3ms)  SELECT `tags`.* FROM `tags` INNER JOIN `sites_tags` ON `tags`.`id` = `sites_tags`.`tag_id` WHERE `sites_tags`.`site_id` = 1
Tag Load (0.4ms)  SELECT `tags`.* FROM `tags` INNER JOIN `sites_tags` ON `tags`.`id` = `sites_tags`.`tag_id` WHERE `sites_tags`.`site_id` = 2

我尝试过的另一件事是

Site.find(1).tags.where("`sites_tags`.`site_id` = 2")

正在生成

SELECT `tags`.* FROM `tags` INNER JOIN `sites_tags` ON `tags`.`id` = `sites_tags`.`tag_id` WHERE `sites_tags`.`site_id` = 1 AND (`sites_tags`.`site_id` = 2)

这不起作用,我认为它试图找到site_id为1和2的单个记录

3 个答案:

答案 0 :(得分:0)

要计算,请尝试:

Site.find(1).tags.count

要获得site1site2中常见的标记计数:

s1 = Site.find(1).tags.map(&:name)

s2 = Site.find(2).tags.map(&:name)

common_tags s1 & s2

答案 1 :(得分:0)

对于数据库级别的解决方案,您可以使用原始sql来获取常用标记的数量:

sql = <<~SQL
  SELECT COUNT(DISTINCT(a.tag_id))
  FROM sites_tags a JOIN sites_tags b ON a.tag_id = b.tag_id
  WHERE a.site_id != b.site_id
  AND a.site_id IN (1, 2);
SQL

count = ActiveRecord::Base.connection.select_rows(sql).flatten
#=> [1]

或者,如果您想要一个名为所有常用标签的数组(并在以后计算),您可以使用此查询:

sql = <<~SQL
  SELECT DISTINCT(c.name)
  FROM sites_tags a
    JOIN sites_tags b ON a.tag_id = b.tag_id
    JOIN tags c ON a.tag_id = c.id
  WHERE a.site_id != b.site_id
  AND a.site_id IN (1, 2);
SQL

tags = ActiveRecord::Base.connection.select_rows(sql).flatten
#=> ["tagB"]

两者都适用于MySQL。

答案 2 :(得分:0)

使用合并

Site.find(1).tags.merge(Site.find(2).tags).count

它将在3个有效查询中完成