sql >> Base de Datos >  >> RDS >> PostgreSQL

Devolver registros duplicados (activerecord, postgres)

Al estilo SQL-y

Primero, resolvamos el problema en SQL, para que la sintaxis específica de Rails no nos engañe.

Esta pregunta SO es un paralelo bastante claro:Encontrar duplicados valores en una tabla SQL

La respuesta de KM (segunda desde arriba, sin marcar, en este momento) cumple con sus criterios de devolver todos los registros duplicados junto con sus ID. He modificado KM's SQL para que coincida con su mesa...

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

La parte dentro de INNER JOIN ( ) es esencialmente lo que ya has generado. Una tabla agrupada de títulos y conteos duplicados. El truco es JOIN ing a las movies sin modificar tabla, que excluirá cualquier película que no tenga coincidencias en la consulta de duplicados.

¿Por qué es tan difícil de generar en Rails? La parte más complicada es que, porque estamos JOIN ing movies a movies , tenemos que crear alias de tabla (m y dupes en mi consulta anterior).

Lamentablemente, Rails no proporciona ninguna forma clara de declarar estos alias. Algunas referencias:

Afortunadamente, dado que tenemos el SQL a mano, podemos usar .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 llamando a Movie.find_by_sql , ActiveRecord asume que nuestro SQL escrito a mano se puede agrupar en Movie objetos. No masajea ni genera nada, lo que nos permite hacer nuestros alias.

Este enfoque tiene sus defectos. Devuelve una matriz y no una relación ActiveRecord, lo que significa que no se puede encadenar con otros ámbitos. Y, en la documentación de find_by_sql método , obtenemos desánimo extra...

Un camino sobre rieles

Realmente, ¿qué está haciendo el SQL arriba? Está obteniendo una lista de nombres que aparecen más de una vez. Luego, compara esa lista con la tabla original. Entonces, hagámoslo usando Rails.

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

Movie.where(title: titles_with_multiple)

Llamamos .keys porque la primera consulta devuelve un hash. Las claves son nuestros títulos. El where() El método puede tomar una matriz, y le hemos entregado una matriz de títulos. Ganador.

Se podría argumentar que una línea de Ruby es más elegante que dos. Y si esa línea de Ruby tiene una cadena impía de SQL incrustada, ¿qué tan elegante es realmente?

¡Espero que esto ayude!