MySQL 또는 Rails에서 특정 날짜 범위 내에서 하루에 AVG를 얻는 가장 좋은 방법

StackOverflow https://stackoverflow.com/questions/423212

문제

예를 들어 주어진 날짜 범위에서 매일 매일 AVG 판매량을 레일로 만들려고합니다.

"sales_price"float 속성이있는 Products_Sold 모델이 있다고 가정 해 봅시다. 그러나 특정 날에 판매가 없다면 (예 : 모델/DB에서는 없음) 단순히 0을 반환하고 싶습니다.

MySQL/Rails 에서이 작업을 완료하는 가장 좋은 방법은 무엇입니까? 나는 다음과 같이 할 수 있다는 것을 안다.

(이 SQL 쿼리는 내가 원하는 것을 얻는 완전히 잘못된 방법 일 수 있습니다.)

SELECT avg(sales_price) AS avg, DATE_FORMAT(created_at, '%m-%d-%Y') AS date
    FROM products_sold WHERE merchant_id = 1 GROUP BY date;

다음과 같은 결과를 얻습니다.

| avg |    date    |
  23    01-03-2009
  50    01-05-2009 
  34    01-07-2009
  ...       ...

내가 얻고 싶은 것은 이것입니다 :

| avg |    date    |
  23    01-03-2009
   0    01-04-2009
  50    01-05-2009
   0    01-06-2009 
  34    01-07-2009
   0    01-08-2009
  ...       ...

SQL 로이 작업을 수행 할 수 있습니까? 아니면 Dateange의 날짜가 SQL 결과 세트에 없는지 찾기 위해 결과를 사후 처리해야합니까? 아마도 하위 선택 사항이 필요하거나 문장이 필요합니까?

모두에게 도움을 주셔서 감사합니다.

도움이 되었습니까?

해결책

ActivereCord에서 내장 그룹 기능 기능을 사용하지 않는 이유가 있습니까? 당신은 "사후 처리"에 대해 걱정하는 것 같습니다.

당신은 레일에 있으므로 아마도 레일 솔루션을 먼저 찾고있을 것입니다 [1]. 내 첫 생각은 같은 일을하는 것입니다

Product.average(:sales_price, :group => "DATE(created_at)", :conditions => ["merchant_id=?", 1])

어떤 ActiveRecord가 설명한 SQL로 바뀌 었습니다. 선언 된 것이 있다고 가정합니다 has_many 상인과 제품 간의 연관성은 아마도 다음과 같은 것을 사용하는 것이 좋습니다.

ave_prices = Merchant.find(1).products.average(:sales_price, :group => "DATE(created_at)")

( "Products_Sold"로서의 모델에 대한 귀하의 설명이 일종의 전사 오류가되기를 바라고 있습니다.

결국, 당신은 당신이 시작한 곳으로 돌아 왔지만, 당신은 더 전통적인 레일 방식으로 도착했습니다 (그리고 Rails는 실제로 규칙을 중요하게 생각합니다!). 이제 우리는 격차를 메워야합니다.

당신의 날짜 범위를 알고 있다고 가정 할 것입니다. 모든 날짜로 정의되었다고 가정 해 봅시다. from_date 에게 to_date.

date_aves = (from_date..to_date).map{|dt| [dt, 0]}

이는 날짜의 전체 목록을 배열로 작성합니다. 우리는 평균을 얻은 날짜가 필요하지 않습니다.

ave_price_dates = ave_prices.collect{|ave_price| ave_price[0]} # build an array of dates
date_aves.delete_if { |dt| ave_price.dates.index(dt[0]) } # remove zero entries for dates retrieved from DB
date_aves.concat(ave_prices)     # add the query results
date_aves.sort_by{|ave| ave[0] } # sort by date

그 롯은 나에게 약간 어수선 해 보인다 : 나는 그것이 간결하고 깨끗할 수 있다고 생각한다. 배열에 머무르기보다는 해시 또는 구조물 건설을 조사했습니다.


1] 나는 SQL을 사용하지 않는다고 말하는 것이 아닙니다. find_by_sql. 괜찮아요

다른 팁

그러한 쿼리의 경우보고하려는 날짜마다 하나의 행이있는 테이블을 생성하는 메커니즘을 찾아야합니다. 그런 다음 분석중인 데이터 테이블과 함께 해당 테이블의 외부 조인을 수행합니다. 널을 0으로 변환하려면 NVL과 함께 플레이하거나 Coalesce를 사용해야 할 수도 있습니다.

어려운 부분은 분석해야 할 범위의 날짜 목록을 포함하는 (임시) 테이블을 생성하는 방법을 작성하는 것입니다. 그것은 DBMS에 따른 것입니다.

그러나 날짜/시간 값을 단일 날짜에 매핑한다는 아이디어는 발견됩니다. 주간 판매를 분석하려는 경우 모든 날짜를 2009 -W01과 같은 ISO 8601 날짜 형식에 매핑해야합니다.

또한 날짜 형식을 2009-01-08 표기법에 매핑하는 것이 좋습니다. 그러면 일반 문자 정렬을 사용하여 날짜 순서로 정렬 할 수 있기 때문입니다.

조금 말리기 :

ave_prices = Merchant.find(1).products.average(:sales_price, :group => "DATE(created_at)")
date_aves = (from_date..to_date).map{|dt| [dt, ave_prices[dt.strftime "%Y-%m-%d"] || 0]}

MySQL은 설정 함수를 설정해야합니까? 즉, 쿼리의 각 행에서 다른 값을 반환하는 함수? PostgreSQL의 예로는 다음을 수행 할 수 있습니다.

select 'foo', generate_series(3, 5);

이렇게하면 2 개의 열과 3 행으로 구성된 결과 세트가 생성되며 왼쪽 열에는 각 행에 'foo'가 포함되어 있고 오른쪽 열에는 3, 4 및 5가 포함됩니다.

따라서 귀하가 동등한 것으로 가정합니다 generate_series() MySQL 및 하위 쿼리 : 필요한 것은 LEFT OUTER JOIN 이 기능에서 이미 가지고있는 쿼리에 이르기까지. 즉, 각 날짜가 출력에 나타납니다.

SELECT
    avg(sales_price) as avg,
    DATE_FORMAT(the_date, '%m-%d-%Y') as date
FROM (select cast('2008-JAN-01' as date) + generate_series(0, 364) as the_date) date_range
LEFT OUTER JOIN products_sold on (the_date = created_at)
WHERE merchant_id = 1
GROUP BY date;

MySQL에 대한 구문을 올바르게 얻으려면 이것으로 약간의 바이올린이 필요할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top