Cách sử dụng SQL-y
Đầu tiên, hãy giải quyết vấn đề trong SQL để cú pháp cụ thể của Rails không đánh lừa chúng ta.
Câu hỏi SO này là một song song khá rõ ràng: Tìm trùng lặp giá trị trong Bảng SQL
Câu trả lời từ KM (thứ hai từ trên cùng, không được đánh dấu, tại thời điểm này) đáp ứng tiêu chí của bạn là trả lại tất cả các bản ghi trùng lặp cùng với ID của chúng. Tôi đã sửa đổi KM's SQL để khớp với của bạn bảng ...
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
Phần bên trong INNER JOIN ( )
về cơ bản là những gì bạn đã tạo. Một bảng được nhóm các tiêu đề và số lượng trùng lặp. Bí quyết là JOIN
nhập nó vào movies
chưa sửa đổi bảng này sẽ loại trừ bất kỳ phim nào không trùng khớp trong truy vấn lừa đảo.
Tại sao điều này rất khó tạo trong Rails? Phần khó nhất là vì chúng ta đang JOIN
ing movies
tới movies
, chúng ta phải tạo bí danh bảng (m
và dupes
trong truy vấn của tôi ở trên).
Đáng buồn thay, nó Rails không cung cấp bất kỳ cách khai báo rõ ràng nào về các bí danh này. Một số tài liệu tham khảo:
- Rails GitHub sự cố đề cập đến "tham gia" và "bí danh". Khốn khổ.
- Câu hỏi SO: truy vấn ActiveRecord với bảng bí danh tên
May mắn thay, vì chúng tôi đã có SQL trong tay, chúng tôi có thể sử dụng .find_by_sql
phương pháp ...
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")
Vì chúng tôi đang gọi Movie.find_by_sql
, ActiveRecord giả sử SQL viết tay của chúng ta có thể được đóng gói vào Movie
các đối tượng. Nó không xoa bóp hay tạo ra bất cứ thứ gì, điều này cho phép chúng tôi tạo bí danh của mình.
Cách tiếp cận này có những thiếu sót của nó. Nó trả về một mảng chứ không phải ActiveRecord Relation, có nghĩa là nó không thể được xâu chuỗi với các phạm vi khác. Và, trong tài liệu cho find_by_sql
phương pháp
, chúng tôi càng thêm chán nản ...
Một cách Rails-y
Thực sự, SQL đang làm gì ở trên? Nó nhận được một danh sách các tên xuất hiện nhiều hơn một lần. Sau đó, nó khớp danh sách đó với bảng gốc. Vì vậy, hãy làm điều đó bằng cách sử dụng Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Chúng tôi gọi .keys
bởi vì truy vấn đầu tiên trả về một hàm băm. Chìa khóa là chức danh của chúng tôi. where()
phương thức có thể nhận một mảng và chúng tôi đã cung cấp cho nó một mảng tiêu đề. Người chiến thắng.
Bạn có thể tranh luận rằng một dòng của Ruby thanh lịch hơn hai dòng. Và nếu một dòng Ruby đó có một chuỗi SQL không ổn định được nhúng bên trong nó, thì nó thực sự thanh lịch đến mức nào?
Hy vọng điều này sẽ hữu ích!