레일스 원시 SQL 예제


239

이 코드를 원시 SQL로 변환하고 레일에서 사용하려면 어떻게해야합니까? heroku에이 코드를 배포 할 때 요청 시간 초과 오류가 발생합니다. raw sql을 사용하면 더 빠를 것이라고 생각합니다.

@payments = PaymentDetail.joins(:project).order('payment_details.created_at desc')
@payment_errors = PaymentError.joins(:project).order('payment_errors.created_at desc')

@all_payments = (@payments + @payment_errors)

3
원시 SQL이 더 빠를 것이라고 생각하는 이유는 무엇입니까? SQL 문제를 어떻게 알 수 있습니까?
John Naegle

쿼리 계획을 보지 않으면 발생한 문제가 created_at에 의해 주문 된 것 같습니다. 아마도 전체 테이블에서 seq 스캔을 수행하고있을 것입니다 (oh 및 프로젝트 테이블도 가져옴). 큰 테이블과 DB가 부족한 DB에서 한 번 컨트롤러 방식으로 두 가지를 수행하면 (heroku 데이터베이스는 일반적으로 조정되며 지출하는 $$에 비해 상대적으로 힘이 부족합니다) 시간 초과가 발생할 수 있습니다. 이 테이블에 많은 삽입 작업을하지 않으면 정렬 된 인덱스를 수행 할 수 있습니다. devcenter.heroku.com/articles/postgresql-indexes#sorted-indexes
Jim Wrubel

1
글쎄, 항상 SQL을 손으로 자르는 것이 더 빠를 것입니다 (당신이하는 일을 알고 있다면). Rails는 정말 불쾌한 SQL을 만듭니다. 잘 작동하지만 일반적이어야합니다 ... 일반적이어야합니다. Jim이 옳을 것이라고 말했다. 인덱싱 생성이 더 좋을 것이다. pgadmin에서 (db와 비교하여) 쿼리를 실행하십시오
baash05

답변:


427

당신은 이것을 할 수 있습니다 :

sql = "Select * from ... your sql query here"
records_array = ActiveRecord::Base.connection.execute(sql)

records_array 그런 다음 반복 할 수있는 배열의 SQL 쿼리 결과입니다.


52
참고 : records_array데이터베이스 어댑터마다 유형이 다릅니다. 당신이 PG를 사용하는 경우, 그것은의 인스턴스 것 PG::Result,하지 Array.
tybro0103

58
그런 다음 결과 배열을 얻으려면 valuesPG::Result객체 를 호출해야합니다.
jobwat

3
@ baash05 "클래스를 통해 액세스하는 것을 선호합니다"는 무슨 뜻입니까? -이것은 모델이없는 테이블에 접근하는 방법에 대해 이야기합니다.
Yo Ludke

1
실제로 OP에서 모델을 사용하지 않는다는 언급은 없습니다. 원시 SQL을 실행하는 방법. PaymentDetail.execute (sql)가 작동합니다.
baash05

20
내가 좋아하는 ActiveRecord::Base.connection.exec_query그것이 반환하기 때문에 더 나은 ActiveRecords::Result같은 편리한 방법이 개체 .columns.rows액세스 헤더와 값을. .executeSUM GROUP BY 절을 실행했을 때 의 해시 배열이 처리하기 어려울 수 있으며 중복 결과를 얻었습니다.
Francio Rodrigues

181

나는 이것이 오래되었다는 것을 알고있다 ... 그러나 나는 오늘 같은 문제가 있었고 해결책을 찾았다.

Model.find_by_sql

결과를 인스턴스화하려면 다음을 수행하십시오.

Client.find_by_sql("
  SELECT * FROM clients
  INNER JOIN orders ON clients.id = orders.client_id
  ORDER BY clients.created_at desc
")
# => [<Client id: 1, first_name: "Lucas" >, <Client id: 2, first_name: "Jan">...]

Model.connection.select_all('sql').to_hash

값의 해시를 원할 경우 :

Client.connection.select_all("SELECT first_name, created_at FROM clients
   WHERE id = '1'").to_hash
# => [
  {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
  {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
]

결과 객체 :

select_allresult객체를 반환 합니다. 당신은 그것으로 마술 일을 할 수 있습니다.

result = Post.connection.select_all('SELECT id, title, body FROM posts')
# Get the column names of the result:
result.columns
# => ["id", "title", "body"]

# Get the record values of the result:
result.rows
# => [[1, "title_1", "body_1"],
      [2, "title_2", "body_2"],
      ...
     ]

# Get an array of hashes representing the result (column => value):
result.to_hash
# => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
      {"id" => 2, "title" => "title_2", "body" => "body_2"},
      ...
     ]

# ActiveRecord::Result also includes Enumerable.
result.each do |row|
  puts row['title'] + " " + row['body']
end

출처 :

  1. SQL에 의한 ActiveRecord-Findinig .
  2. Ruby on Rails-Active 레코드 결과 .

9
#find_by_sql정확히 내가 원하는 것입니다, 정말 감사합니다.
Shelvacu

#find_by_sql입니다!
Steven L.

28

을 사용하여 원시 쿼리를 실행할 수 있습니다 ActiveRecord. 그리고 나는 SQL 블록과 함께 갈 것을 제안합니다

query = <<-SQL 
  SELECT * 
  FROM payment_details
  INNER JOIN projects 
          ON projects.id = payment_details.project_id
  ORDER BY payment_details.created_at DESC
SQL

result = ActiveRecord::Base.connection.execute(query)

안녕하세요 ..이 검색어에 매개 변수를 전달할 수있는 방법이 있습니까?
Akshay Goyal

@AkshayGoyal 블록 내에서 일반적인 루비 문자열 보간을 사용할 수 있습니다. SELECT * FROM users WHERE users.id = #{ user.id }
Nathan Beck

2
당신이 할 수 있지만 그것은 SQL 주입의 가능성에 노출 될 수 있음
디팍 Mahakale

26

두 테이블 모두에 대해 단일 쿼리를 갖도록 SQL을 지시 할 수 있습니다. 이 예제가 필요를 지정하지 않았더라도 사람들이 변수를 문자열 자체에 직접 넣지 않도록 (예 : SQL 주입 위험) 위생 화 된 쿼리 예제를 제공합니다.

@results = []
ActiveRecord::Base.connection.select_all(
  ActiveRecord::Base.send(:sanitize_sql_array, 
   ["... your SQL query goes here and ?, ?, ? are replaced...;", a, b, c])
).each do |record|
  # instead of an array of hashes, you could put in a custom object with attributes
  @results << {col_a_name: record["col_a_name"], col_b_name: record["col_b_name"], ...}
end

편집 : Huy가 말했듯이 간단한 방법은 ActiveRecord::Base.connection.execute("...")입니다. 다른 방법은 ActiveRecord::Base.connection.exec_query('...').rows입니다. 포스트 그레스를 사용하는 경우 그리고 당신은 예를 들어, 기본 준비된 문을 사용하여 준비된 문을 준비 raw_connection 수행 할 수 있습니다, 그리고에 설명 된대로 exec_prepared https://stackoverflow.com/a/13806512/178651

또한 원시 SQL 조각을 ActiveRecord 관계형 쿼리 ( http://guides.rubyonrails.org/active_record_querying.html) 및 연관, 범위 등으로 넣을 수 있습니다. ActiveRecord 관계형 쿼리를 사용하여 동일한 SQL을 구성 할 수 있습니다. Ernie가 http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel/ 에서 언급 한 ARel . 물론 다른 ORM, 보석 등도 있습니다.

이것이 많이 사용될 것이며 인덱스를 추가해도 다른 성능 / 자원 문제가 발생하지 않으면 payment_details.created_at 및 payment_errors.created_at에 대한 인덱스를 DB에 추가하십시오.

많은 레코드가 있고 모든 레코드가 한 번에 나타나지 않아도되는 경우 페이지 매김 사용을 고려하십시오.

페이지를 매길 필요가있는 경우, payment_details 및 payment_errors 테이블을 결합한 payment_records라는 뷰를 DB에 먼저 작성한 다음 뷰에 대한 모델 (읽기 전용)을 작성하십시오. 일부 DB는 구체화 된 뷰를 지원하므로 성능에 좋습니다.

또한 Rails 서버 및 DB 서버, 구성, 디스크 공간, 네트워크 속도 / 대기 시간 등의 하드웨어 또는 VM 사양, 근접성 등을 고려하고 그렇지 않은 경우 Rails 앱과 다른 서버 / VM에 DB를 배치하는 것을 고려하십시오. .


asker가 날짜별로 정렬되어 있기 때문에 페이지 매김이 도움이되지 않을 것이라고 생각합니까? 나는 SQL에 대해 가장 깊이있는 것은 아니지만 원래 게시물의 쿼리는 정렬하기 위해 전체 테이블을 가져와야한다는 것만 이해합니다. 그런 다음에 만 결과를 제한 할 수 있습니다. 대량의 쿼리 계획이 order 절에 있다고 생각합니다.
Jim Wrubel

@JimWrubel은 페이지 매김에 대한 단락을 명확히했습니다.
게리 S. 위버

2

나는 작업 할 exec_queryActiveRecord이 객체로 변신 쿼리의 매핑을 반환하기 때문에 피사체가 원시 SQL 때 그래서 객체로 반복하는 매우 실용적이고 생산적인 도착, 클래스입니다.

예:

values = ActiveRecord::Base.connection.exec_query("select * from clients")
p values

이 완전한 쿼리를 반환하십시오.

[{"id": 1, "name": "user 1"}, {"id": 2, "name": "user 2"}, {"id": 3, "name": "user 3"}]

값 목록 만 가져 오려면

p values.rows

[[1, "user 1"], [2, "user 2"], [3, "user 3"]]

필드 열만 가져 오려면

p values.columns

["id", "name"]

0

예를 들어 조건에서 함수를 호출하려는 경우 원시 SQL을 ActiveRecord 조건과 혼합 할 수도 있습니다.

my_instances = MyModel.where.not(attribute_a: nil) \
  .where('crc32(attribute_b) = ?', slot) \
  .select(:id)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.