一种SQL方法
首先,让我们只在SQL中解决问题,这样Rails特定的语法就不会欺骗我们。
这个SO问题是一个相当明显的并行: 在SQL表中查找重复值
目前排名第二的KM的答案(非选中标记)符合您返回所有重复记录及其ID的标准。 我修改了KM的SQL以匹配您的表格...
SELECT
m.id, m.title
FROM
movies m
INNER JOIN (
SELECT
title, COUNT(*) AS CountOf
FROM
movies
GROUP BY
title
HAVING COUNT(*)>1
) dupes
ON
m.title=dupes.title
INNER JOIN ( )
内的部分本质上就是您已生成的内容。一个由重复标题和计数组成的分组表。诀窍在于将其与未修改的movies
表连接起来,这将排除任何在重复查询中没有匹配项的电影。
为什么在Rails中生成这样的内容很难?最棘手的部分是,因为我们正在将movies
连接到movies
,所以我们必须创建表别名(如我上面的查询中的m
和dupes
)。
遗憾的是,Rails没有提供任何干净的方法来声明这些别名。一些参考:
幸运的是,既然我们已经有了SQL代码,我们可以使用.find_by_sql
方法...
Movie.find_by_sql("SELECT m.id, m.title FROM movies m INNER JOIN (SELECT title, COUNT(*) FROM movies GROUP BY title HAVING COUNT(*)>1) dupes ON m.first=.first")
因为我们调用了Movie.find_by_sql
,ActiveRecord假设我们手写的SQL可以绑定到Movie
对象中。它不会对任何内容进行调整或生成,这使我们能够使用别名。
这种方法有其缺点。它返回一个数组而不是ActiveRecord Relation,这意味着它不能与其他作用域链接。在find_by_sql方法的文档中,我们还得到额外的劝阻...
这应该是最后的选择,因为例如使用MySQL特定术语将锁定您使用特定数据库引擎或要求您更改调用(如果切换引擎)。
Rails的方式
实际上,上面的SQL在做什么?它正在获取出现多次的名称列表。然后,它将该列表与原始表进行匹配。所以,让我们只需使用Rails来完成这个操作。
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
我们使用 .keys
是因为第一个查询返回了一个哈希表。这些键是我们的标题。 where()
方法可以接受一个数组,我们已经将标题数组传递给它。赢家。
你可以认为一行 Ruby 代码比两行更加优雅。但如果那一行 Ruby 代码中嵌入了令人难以置信的 SQL 字符串,它真的优雅吗?
希望这有帮助!