sql >> Database >  >> RDS >> PostgreSQL

Dubbele records retourneren (activerecord, postgres)

Een SQL-achtige manier

Laten we eerst het probleem in SQL oplossen, zodat de Rails-specifieke syntaxis ons niet voor de gek houdt.

Deze SO-vraag is een vrij duidelijke parallel:Duplicate vinden waarden in een SQL-tabel

Het antwoord van KM (tweede van boven, op dit moment niet aangevinkt) voldoet aan uw criteria om alle dubbele records samen met hun ID's te retourneren. Ik heb KM's gewijzigd SQL die overeenkomt met uw tafel...

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

Het gedeelte binnen de INNER JOIN ( ) is in wezen wat je al hebt gegenereerd. Een gegroepeerde tabel met dubbele titels en tellingen. De truc is JOIN naar de ongewijzigde movies tabel, die alle films uitsluit die geen overeenkomsten hebben in de zoekopdracht naar dupes.

Waarom is dit zo moeilijk te genereren in Rails? Het lastigste is dat, want we zijn JOIN movies spelen naar movies , moeten we tabelaliassen maken (m en dupes in mijn vraag hierboven).

Helaas biedt het Rails geen schone manieren om deze aliassen te declareren. Enkele referenties:

Gelukkig, aangezien we de SQL in de hand hebben, kunnen we de .find_by_sql gebruiken methode...

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")

Omdat we Movie.find_by_sql . noemen , gaat ActiveRecord ervan uit dat onze handgeschreven SQL kan worden gebundeld in Movie voorwerpen. Het masseert of genereert niets, waardoor we onze aliassen kunnen gebruiken.

Deze aanpak heeft zijn tekortkomingen. Het retourneert een array en geen ActiveRecord-relatie, wat betekent dat het niet kan worden gekoppeld aan andere scopes. En, in de documentatie voor de find_by_sql methode , krijgen we extra ontmoediging...

A Rails-y Way

Echt, wat doet de SQL hierboven? Het krijgt een lijst met namen die meer dan eens voorkomen. Vervolgens vergelijkt het die lijst met de originele tabel. Laten we dat dus gewoon doen met Rails.

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

Movie.where(title: titles_with_multiple)

We noemen .keys omdat de eerste query een hash retourneert. De sleutels zijn onze titels. De where() methode kan een array aannemen, en we hebben het een array van titels gegeven. Winnaar.

Je zou kunnen stellen dat één regel Ruby eleganter is dan twee. En als die ene regel Ruby een goddeloze reeks SQL bevat, hoe elegant is het dan eigenlijk?

Ik hoop dat dit helpt!



  1. AOL/J Setup Testsuite

  2. In MySQL:Hoe geef ik een tabelnaam door als opgeslagen procedure en/of functieargument?

  3. PL/SQL-vergelijkingsfout in trigger (PLS-00405)

  4. Hoe gebruik ik PostgreSQL JSON(B)-operators die een vraagteken bevatten? via JDBC