返回重复记录(activerecord,postgres)

Ash*_*ury 7 postgresql activerecord ruby-on-rails

我有以下查询返回重复的标题,但是: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">]
Run Code Online (Sandbox Code Playgroud)

我尝试添加:id到select和group但它返回一个空数组.如何归还整个电影记录,而不仅仅是标题?

Lan*_*ose 15

一种SQL-y方式

首先,让我们解决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
Run Code Online (Sandbox Code Playgroud)

里面的部分INNER JOIN ( )基本上就是你已经生成的部分.重复标题和计数的分组表.诀窍在于JOIN未经修改的movies表,它将排除任何在dupes查询中没有匹配的电影.

为什么在Rails中生成这么难?最棘手的部分是,因为我们JOIN荷兰国际集团moviesmovies,我们要创建表别名(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")
Run Code Online (Sandbox Code Playgroud)

因为我们正在调用Movie.find_by_sql,所以ActiveRecord假设我们的手写SQL可以捆绑到Movie对象中.它不会按摩或产生任何东西,这可以让我们做别名.

这种方法有其缺点.它返回一个数组而不是ActiveRecord Relation,这意味着它不能与其他范围链接.并且,在该find_by_sql方法的文档中,我们得到额外的沮丧......

这应该是最后的手段,因为使用例如MySQL特定术语将锁定您使用该特定数据库引擎或要求您在切换引擎时更改您的呼叫.

一种Rails-y方式

真的,上面的SQL是做什么的?它获得了不止一次出现的名称列表.然后,它将该列表与原始表匹配.所以,让我们使用Rails来做到这一点.

titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys

Movie.where(title: titles_with_multiple)
Run Code Online (Sandbox Code Playgroud)

我们调用.keys因为第一个查询返回一个哈希.钥匙是我们的头衔.该where()方法可以采用数组,我们已经为它提供了一系列标题.优胜者.

你可以说一行Ruby优于两行.如果那一行Ruby中嵌入了一个不成熟的SQL字符串,它真的有多优雅?

希望这可以帮助!