返回重复记录(activerecord,postgres)

8
我有以下查询返回重复的标题,但是:idnil:
Movie.select(:title).group(:title).having("count(*) > 1")

[#<Movie:0x007f81f7111c20 id: nil, title: "Fargo">,
#<Movie:0x007f81f7111ab8 id: nil, title: "Children of Men">,
#<Movie:0x007f81f7111950 id: nil, title: "The Martian">,
#<Movie:0x007f81f71117e8 id: nil, title: "Gravity">]

我尝试将:id添加到选择和分组中,但返回一个空数组。如何返回整个电影记录,而不仅仅是标题?
2个回答

15

一种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,所以我们必须创建表别名(如我上面的查询中的mdupes)。

遗憾的是,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 字符串,它真的优雅吗?

希望这有帮助!


非常棒的答案,超级有启发性!在 .where() 中使用数组的技巧非常酷,我本来会用笨拙的 each 循环。 - Ashbury
很高兴能帮忙! :) - Lanny Bose

-2
你可以尝试在你的

1
我应该提到我尝试过那个,但是我得到了“PG :: GroupingError:ERROR:列'movies.id'必须出现在GROUP BY子句中或用于聚合函数”。 - Ashbury
也许你还需要将你的ID添加到你的组中。我已经更新了它。 - akbarbin
1
这会返回一个空数组,我觉得是因为它也在寻找重复的:id。:( 谢谢你的帮助。 - Ashbury
1
在我的示例中,所有的id都是nil。我想找到重复的:title,但返回整个电影记录。 - Ashbury
1
抱歉,伙计,它就是不起作用。我尝试使用count(title)。将:id添加到select会导致PG :: GroupingError,将:id添加到两者都会得到空数组。 - Ashbury
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接