PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Retornar registros duplicados (activerecord, postgres)


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:

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!