Uma maneira SQL-y
Primeiro, vamos resolver o problema em SQL, para que a sintaxe específica do Rails não nos engane.
Esta pergunta SO é um paralelo bastante claro:Encontrando duplicata valores em uma tabela SQL
A resposta de KM (segundo a partir do topo, não marcada, no momento) atende ao seu critério de retornar todos os registros duplicados junto com seus IDs. Modifiquei KMs SQL para corresponder seu tabela...
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
A parte dentro do
INNER JOIN ( )
é essencialmente o que você já gerou. Uma tabela agrupada de títulos e contagens duplicadas. O truque é JOIN
transferindo-o para os movies
não modificados table, que excluirá quaisquer filmes que não tenham correspondências na consulta de dupes. Por que isso é tão difícil de gerar no Rails? A parte mais complicada é que, porque estamos
JOIN
ing movies
para movies
, temos que criar aliases de tabela (m
e dupes
na minha consulta acima). Infelizmente, o Rails não fornece nenhuma maneira limpa de declarar esses aliases. Algumas referências:
- Problemas do Rails GitHub mencionando "juntar" e "alias". Miséria.
- SO Question:consulta ActiveRecord com tabela de alias nomes
Felizmente, já que temos o SQL em mãos, podemos usar o
.find_by_sql
método... 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")
Porque estamos chamando
Movie.find_by_sql
, o ActiveRecord assume que nosso SQL escrito à mão pode ser empacotado em Movie
objetos. Não massageia nem gera nada, o que nos permite fazer nossos aliases. Essa abordagem tem suas deficiências. Ele retorna um array e não uma relação ActiveRecord, o que significa que não pode ser encadeado com outros escopos. E, na documentação do
find_by_sql
método
, ficamos com um desânimo extra... Uma maneira Rails-y
Realmente, o que o SQL está fazendo acima? Está recebendo uma lista de nomes que aparecem mais de uma vez. Então, ele está comparando essa lista com a tabela original. Então, vamos fazer isso usando Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Chamamos
.keys
porque a primeira consulta retorna um hash. As chaves são nossos títulos. O where()
pode receber um array, e entregamos a ele um array de títulos. Vencedora. Você poderia argumentar que uma linha de Ruby é mais elegante do que duas. E se essa linha de Ruby tem uma string ímpia de SQL embutida nela, quão elegante ela é realmente?
Espero que isto ajude!