Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

임의의 순서로 데이터베이스 레코드를 선택하는 방법

Rails에서는 ID가 있는 경우 데이터베이스에서 많은 레코드를 쉽게 얻을 수 있습니다.

Person.where(id: [1, 2, 3]).map(&:id) => [1, 2, 3]

하지만 레코드를 다른 주문? 검색 엔진이 가장 관련성이 높은 ID를 먼저 반환할 수도 있습니다. 어떻게 유지합니까? 당신의 기록은 순서대로?

where를 시도할 수 있습니다. 다시:

Person.where(id: [2, 1, 3]).map(&:id) => [1, 2, 3]

그러나 그것은 전혀 작동하지 않습니다. 그래서 어떻게 기록을 올바른 순서로 되돌리셨나요?

호환 방식:case 진술

Ruby와 마찬가지로 SQL은 case...when을 지원합니다. 진술.

자주 볼 수는 없지만 case...when 문장은 거의 해시처럼 행동합니다. 한 값을 다른 값에 매핑할 수 있습니다.

case :b
when :a then 1
when :b then 2
when :c then 3
end # => 2

이 case 문은 해시처럼 보입니다.

{
  :a => 1,
  :b => 2,
  :c => 3
}[:b] # => 2

따라서 키가 표시되어야 하는 순서에 따라 키를 매핑하는 방법이 있습니다. 그리고 데이터베이스는 임의의 순서로 결과를 정렬하고 반환할 수 있습니다.

이를 알고 있으면 ID와 해당 위치를 case에 넣을 수 있습니다. 명령문을 작성하고 SQL order에서 사용합니다. 조항.

따라서 [2, 1, 3] 순서로 개체를 반환하려는 경우 SQL은 다음과 같을 수 있습니다.

SELECT * FROM people
  WHERE id IN (1, 2, 3)
  ORDER BY CASE id
    WHEN 2 THEN 0
    WHEN 1 THEN 1
    WHEN 3 THEN 2
    ELSE 3 END;

그렇게 하면 레코드가 올바른 순서로 반환됩니다. case 각 ID를 반환해야 하는 순서로 변환합니다.

물론, 그것은 우스꽝스럽게 보입니다. 그리고 그런 조항을 손으로 만드는 것이 얼마나 성가신 일인지 상상할 수 있을 것입니다.

하지만 없다. 손으로 구축합니다. 이것이 Ruby의 장점입니다.

lib/extensions/active_record/find_by_ordered_ids.rb
module Extensions::ActiveRecord::FindByOrderedIds
  extend ActiveSupport::Concern
  module ClassMethods
    def find_ordered(ids)
      order_clause = "CASE id "
      ids.each_with_index do |id, index|
        order_clause << sanitize_sql_array(["WHEN ? THEN ? ", id, index])
      end
      order_clause << sanitize_sql_array(["ELSE ? END", ids.length])
      where(id: ids).order(order_clause)
    end
  end
end

ActiveRecord::Base.include(Extensions::ActiveRecord::FindByOrderedIds)

Person.find_ordered([2, 1, 3]) # => [2, 1, 3]

정확히 우리가 원했던 방식입니다!

더 깨끗하고 MySQL 전용 방식

MySQL을 사용하는 경우 더 깔끔한 방법이 있습니다. MySQL에는 특별한 ORDER BY FIELD가 있습니다. 구문:

SELECT * FROM people
WHERE id IN (1, 2, 3)
ORDER BY FIELD(id, 2, 1, 3);

Ruby에서 생성할 수도 있습니다.

lib/extensions/active_record/find_by_ordered_ids.rb
module Extensions::ActiveRecord::FindByOrderedIds
  extend ActiveSupport::Concern
  module ClassMethods
    def find_ordered(ids)
      sanitized_id_string = ids.map {|id| connection.quote(id)}.join(",")
      where(id: ids).order("FIELD(id, #{sanitized_id_string})")
    end
  end
end

ActiveRecord::Base.include(Extensions::ActiveRecord::FindByOrderedIds)

따라서 MySQL을 사용하고 있고 호환성에 대해 너무 걱정하지 않는다면 이것이 좋은 방법입니다. 이러한 진술이 로그를 통해 전달되기 때문에 훨씬 더 읽기 쉽습니다.

특정 임의의 순서로 레코드를 표시하려면 Ruby에서 정렬할 필요가 없습니다. 약간의 코드 스니펫만 있으면 데이터베이스에서 데이터를 찾고, 정렬하고, 앱으로 반환하는 작업을 수행할 수 있습니다.