이러한 종류의 쿼리를 작성하는 일반적인 경험 법칙은 "Ruby-land"에서의 작업을 최소화하고 "Database-land"에서의 작업을 극대화하는 것입니다. 위의 솔루션에서 세트 array
에있는 모든 태그로 표식 집합을 가져 오는데 이는 아마도 매우 큰 집합 (태그가있는 모든 게시물) 일 것입니다. 이것은 루비 배열로 표현되고 처리됩니다 (group_by
은 Ruby-world에, group
은 Database-land에서와 동일합니다).
읽기가 어렵다는 점을 제외하면 그 해결책은 많은 표식 집합에서 느려질 것입니다.
루비 세계에서 무거운 것을하지 않고도 문제를 해결할 수있는 몇 가지 방법이 있습니다. 한 가지 방법은 다음과 같이 하위 쿼리를 사용 :이 모든 것을 여기부터 SQL에서 직접 계산되어이 같은 쿼리 생성 (tag_ids 5 다시 및 8)
SELECT "posts".*
FROM "posts"
WHERE "posts"."id" IN (SELECT "posts"."id" FROM "posts" INNER JOIN "markings" ON "markings"."post_id" = "posts"."id" WHERE "markings"."tag_id" = 5)
AND "posts"."id" IN (SELECT "posts"."id" FROM "posts" INNER JOIN "markings" ON "markings"."post_id" = "posts"."id" WHERE "markings"."tag_id" = 8)
주
scope :with_tag_ids, ->(tag_ids) {
tag_ids.map { |tag_id|
joins(:markings).where(markings: { tag_id: tag_id })
}.reduce(all) { |scope, subquery| scope.where(id: subquery) }
}
, 어떤 배열 없다 루비에서 생성되거나 처리됩니다. 이것은 일반적으로 훨씬 더 잘 확장됩니다.
또는, 서브 쿼리하지 않고 단일 쿼리를 COUNT
을 사용하여 수행 할 수 있습니다
다음과 같은 SQL을 생성
scope :with_tag_ids, ->(tag_ids) {
joins(:markings).where(markings: { tag_id: tag_ids }).
group(:post_id).having('COUNT(posts.id) = ?', tag_ids.count)
}
: 이것은 여러 표시가없는 것으로 가정
SELECT "posts".*
FROM "posts"
INNER JOIN "markings" ON "markings"."post_id" = "posts"."id"
WHERE "markings"."tag_id" IN (5, 8)
GROUP BY "post_id"
HAVING (COUNT(posts.id) = 2)
같은 쌍의 tag_id
과 post_id
은 카운트를 버리게합니다.
마지막 솔루션이 아마도 가장 효율적이라고 생각하지만 다른 솔루션을 시도하고 데이터에 가장 적합한 것이 무엇인지 확인해야합니다.
다음을 참조하십시오. Query intersection with activerecord
이와 비슷한 방법을 시도해 볼 수 있습니다. http://stackoverflow.com/questions/25606775/how-to-find-posts-tagged-with-more-than-one-tag-in-rails-and-postgresql – Jayaprakash