7

레거시 데이터베이스를 레일즈 애플리케이션 (3.2.3)으로 마이그레이션하는 중입니다. 원래 데이터베이스에는 보고서에 대한 몇 가지 긴 SQL 쿼리가 제공됩니다. 지금은 레일즈 애플리케이션에서 sql 쿼리를 사용하고 싶습니다. 그리고 나서 한 번에 하나씩 (시간이 허용 될 때) SQL 쿼리를 '적절한'레일스 쿼리로 바꿉니다. Rails 3 애플리케이션에서 원시 SQL 쿼리 사용?

는 전 임상 모델이 컨트롤러는 다음과 같은 코드를 가지고 : 그 코드를 실행하면

@clinical_income_by_year = Clinical.find_all_by_sql(SELECT date_format(c.transactiondate,'%Y') as Year, 
               date_format(c.transactiondate,'%b') as Month, 
               sum(c.LineBalance) as "Income" 
               FROM clinical c 
               WHERE c.Payments = 0 AND c.LineBalance <> 0 
               AND c.analysiscode <> 213 
               GROUP BY c.MonthYear;) 

는 그러나, 나는 몇 가지 오류가 서식 할 수 있습니다.

Started GET "/clinicals" for 127.0.0.1 at 2012-04-29 18:00:45 +0100 

SyntaxError (/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:6: syntax error, unexpected tIDENTIFIER, expecting ')' 
...rmat(c.transactiondate,'%Y') as Year, 
...        ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:7: syntax error, unexpected tIDENTIFIER, expecting keyword_end 
...rmat(c.transactiondate,'%b') as Month, 
...        ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:8: syntax error, unexpected tIDENTIFIER, expecting keyword_end 
...   sum(c.LineBalance) as "Income" 
...        ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:10: syntax error, unexpected tCONSTANT, expecting keyword_end 
...  WHERE c.Payments = 0 AND c.LineBalance <> 0 
...        ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:10: syntax error, unexpected '>' 
...yments = 0 AND c.LineBalance <> 0 
...        ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:11: syntax error, unexpected '>' 
...   AND c.analysiscode <> 213 
...        ^

SQL 쿼리를 컨트롤러로 가져 오기 전에해야 할 일이 있습니까? 쿼리에 문제가있을 수도 있지만 (데이터베이스가 꽤 오래 전에 작성되었습니다) 데이터베이스 내에서 직접 실행될 때 예상대로 작동합니다. 다음과 같은 배열을 반환합니다 :

---------------------------------------------- 
| Year  | Month  |  Income  | 
---------------------------------------------- 
---------------------------------------------- 
| 2012  | January | 20,000  | 
| 2012  | February | 20,000  | 
| 2012  | March  | 20,000  | 
| 2012  | April  | 20,000  | 
---------------------------------------------- 
etc.. 

모든 도움말, 조언 또는 일반적인 포인터를 주시면 감사하겠습니다!

올바른 레일스 쿼리로 SQL 쿼리를 변환하려고 시도한 결과 http://guides.rubyonrails.org/active_record_querying.html입니다.

은 지금까지 나는 마지막 줄에 두 번째 일치 한 :

AND c.analysiscode <> 213 

아기 단계

@clinical_income_by_year = Clinical.where("AnalysisCode != 213") 

와 함께! 내가 필터링을 가지고

UPDATE, 지금은 레일 가이드 사이트 덕분에 분류하지만 난 SQL 쿼리의 그룹화 및 합계 부분에 붙어있어. 나는 지금까지 다음과 같은 한 :

@clinical_income_by_year = Clinical.where("AnalysisCode != 213 AND Payments != 0 AND LineBalance != 0").page(params[:page]).per_page(15) 

나는 SQL 쿼리의 다음 두 줄에 구축 사투를 벌인거야 :

sum(c.LineBalance) as "Income" 

GROUP BY c.MonthYear;) 

내보기 코드의 모습 이 :

<% @clinical_income_by_year.each do |clinical| %> 
    <tr> 
    <td><%= clinical.TransactionDate.strftime("%Y") %></td> 
    <td><%= clinical.TransactionDate.strftime("%B") %></td> 
    <td><%= Clinical.sum(:LineBalance) %></td> 
    </tr>  
    <% end %> 
</table> 
    <%= will_paginate @clinical_income_by_year %> 

답변

14

R 당신이 포함 된 따옴표에 대해 걱정하지 않도록

@clinical_income_by_year = Clinical.find_by_sql(%q{ ... }) 

내가 이것에 대한 (당신이 보간을 필요로하는 경우) %q 또는 %Q을 사용하는 것이 좋습니다 것 : uby 파서는 문자열을 사용해야 SQL을 이해하지 않습니다 순전히.또한 모델에서 클래스 메서드로 이동하여 컨트롤러가 자신의 비즈니스가 아닌 것을 걱정하지 않도록해야합니다. 그러면 connection.quote 및 친구들에게 쉽게 액세스하여 문자열 보간을 올바르게 사용할 수 있습니다.

find_by_sql(%Q{ 
    select ... 
    from ... 
    where x = #{connection.quote(some_string)} 
}) 

또한, 귀하의 SQL에서 세미콜론

GROUP BY c.MonthYear;}) 

필요가 없습니다. 일부 데이터베이스는 처리 할 수 ​​있지만 어쨌든 제거해야합니다.

데이터베이스에 따라 식별자 (테이블 이름, 열 이름, ...)는 대문자와 소문자를 구별해야합니다 (일부 증오심이있는 사람이 생성시 인용하지 않는 한) 소문자 열 이름을 사용하여 레일스에 더 잘 들어 맞도록하십시오.

SELECT에 집계 또는 그룹화되지 않은 열이 있으므로 일부 데이터베이스는 GROUP BY를 좋아하지 않으므로 각 그룹에 대해 c.transactiondate을 사용하는 모호성이 있습니다.


쿼리에 대한보다 "Railsy"버전은 다음과 같이 보일 것입니다 :

@c = Clinical.select(%q{date_format(transactiondate, '%Y') as year, date_format(transactiondate, '%b') as month, sum(LineBalance) as income}) 
      .where(:payments => 0) 
      .where('linebalance <> ?', 0) 
      .where('analysiscode <> ?', 213) 
      .group(:monthyear) 

그런 다음이 같은 일을 할 수있는 :

@c.each do |c| 
    puts c.year 
    puts c.month 
    puts c.income 
end 

결과를 액세스 할 수 있습니다. 또한 루비로 날짜 맹 글링을 눌러 조금 단순화 수 :

@c = Clinical.select(%q{c.transactiondate, sum(c.LineBalance) as income}) 
      .where(:payments => 0) 
      .where('linebalance <> ?', 0) 
      .where('analysiscode <> ?', 213) 
      .group(:monthyear) 

그럼 오히려 c.yearc.month를 호출하는 대신 루비에서 떨어져 c.transactiondate 당깁니다.

+0

SQL 쿼리를 Rails 쿼리로 변환하는 과정을 시작 했으므로 모델 및 컨트롤러에서 SQL 쿼리를 계속 사용 하시겠습니까? – dannymcc

+1

@dannymcc : 일반적으로 Rails 메서드를 사용하는 것이 좋습니다. 그러나 저장 프로 시저, 창 함수, 파생 테이블, CTE 등과 같은 "고급"데이터베이스 기능을 사용하는 경우 SQL이 필요한 경우가 있습니다. 그런 다음 많은 원시 작업을 완료하기위한 SQL; 모든 쿼리가 간단하다면 'select * from t1 where ...'질의를하면 ActiveRecord 메소드를 사용하는 것이 더 나을 것입니다. 그것은 판결문이며, 당신과 당신의 코드를 유지하는 사람들에게 더 명확한 것을 사용하십시오. –

+0

ActiveRecord 메서드를 사용하는 것은 나를 읽을 때 더 명확하다고 생각하지만, 쓰기는 원시 SQL 쿼리를 작성하는 것과 같습니다. 조언 해주셔서 감사합니다. – dannymcc