2016-04-24 99 views
1

LaserSheetモデルの統計を計算して、ダッシュボードページのmorris.jsチャートを作成します。私は現在、それは1つの統計で作業している:私は、歴史的に追加したいRails:1日ごとに統計とグループを計算する

# Show four Mondays ago up to this coming Sunday (4 weeks) 
start_date = Time.zone.now.beginning_of_week - 3.weeks 
end_date = Time.zone.now.end_of_week 

# Calculate sheets cut per day 
empty_dates_hash = Hash[(start_date.to_date..end_date.to_date).collect { |v| [v, 0] }] 
recent_cut_stats = LaserSheet.where('cut_at IS NOT NULL') 
          .where('cut_at > ?', start_date.beginning_of_day) 
          .where('cut_at < ?', end_date.end_of_day) 
          .group("DATE(cut_at::TIMESTAMPTZ AT TIME ZONE '#{Time.zone.now.formatted_offset}'::INTERVAL)") 
          .count 
recent_cut_stats = empty_dates_hash.merge(recent_cut_stats) 

日でグループ化されたSTATを「シートをカットする左」。これを達成するには、その日にcreated_atだったLaserSheetsをすべて検索する必要があります。cut_atは、その日付よりもNULLまたはそれ以降です。

私は手動で昨日のためにこれを行うことができます。

LaserSheet.where('created_at < ?', Time.zone.yesterday.end_of_day) 
      .where('cut_at IS NULL OR cut_at > ?', Time.zone.yesterday.end_of_day) 
      .count 

と今日のため:

LaserSheet.where('created_at < ?', Time.zone.today.end_of_day) 
      .where('cut_at IS NULL OR cut_at > ?', Time.zone.today.end_of_day) 
      .count 

私は[start_date..end_date]に毎日のためにこれを繰り返すことができますが、これは非常に非効率的です。 1つのデータベースクエリでこれを達成する方法はありますか?単純に一日ごとにグループ化するのと同じくらい些細なことではありません。

私はリモートで効率的な、あるいは構文的に有効なRubyのコードを書くことができませんPostgreSQLとRailsの4

答えて

0

を使用しますが、ここであなたを助けるかもしれないいくつかの生のSQLでいます。

あなたは、日付のリストを生成するためにgenerate_seriesを使用して、このリストに参加残さなければならない:

SELECT * 
FROM generate_series(
    date_trunc('day', now()) - CAST('7 day' AS interval), 
    date_trunc('day', now()), 
    CAST('1 day' AS interval) 
); 

    generate_series 
------------------------ 
2016-04-17 00:00:00+00 
2016-04-18 00:00:00+00 
2016-04-19 00:00:00+00 
2016-04-20 00:00:00+00 
2016-04-21 00:00:00+00 
2016-04-22 00:00:00+00 
2016-04-23 00:00:00+00 
2016-04-24 00:00:00+00 
(8 rows) 

を今、あなたは、日付の束を生成する方法を知っていることを、すべてあなたがしなければならないとこれらに参加しています正しい句。

しかし、最初に、我々はいくつかのテストデータを必要とする:

SELECT 
    CAST(created_at AS timestamp), 
    CAST(cut_at AS timestamp) 
FROM (
    VALUES 
     ('2016-04-20', null),   /* not cut yet */ 
     ('2016-04-20', '2016-04-22'), /* cut 2 days ago */ 
     ('2016-04-20', null),   /* not cut yet */ 
     ('2016-04-23', '2016-04-23'), /* cut yesterday */ 
     ('2016-04-23', null),   /* not cut yet */ 
     ('2016-04-24', '2016-04-24'), /* cut today */ 
     ('2016-04-24', '2016-04-26') /* cut tomorrow (because I can :p) */ 
) as laser_sheet(created_at, cut_at); 


    created_at  |  cut_at 
---------------------+--------------------- 
2016-04-20 00:00:00 | 
2016-04-20 00:00:00 | 2016-04-22 00:00:00 
2016-04-20 00:00:00 | 
2016-04-23 00:00:00 | 2016-04-23 00:00:00 
2016-04-23 00:00:00 | 
2016-04-24 00:00:00 | 2016-04-24 00:00:00 
2016-04-24 00:00:00 | 2016-04-26 00:00:00 
(7 rows) 

、最終クエリは次のようになります。

WITH date_serie AS (
    /* generate one row by day for the last 7 days */ 
    SELECT generate_series as day 
    FROM generate_series(
     /* replace "CAST('2016-04-24 16:56:23' AS datetime)" with "now()" to get a dynamic view */ 
     date_trunc('day', CAST('2016-04-24 16:56:23' AS timestamp)) - CAST('7 day' AS interval), 
     date_trunc('day', CAST('2016-04-24 16:56:23' AS timestamp)), 
     CAST('1 day' AS interval) 
    ) 
), 
laser_sheet AS (
    /* below is some test data */ 
    SELECT 
     CAST(created_at AS timestamp) AS created_at, 
     CAST(cut_at AS timestamp) AS cut_at 
    FROM (
     VALUES 
      ('2016-04-20', null),   /* not cut yet */ 
      ('2016-04-20', '2016-04-22'), /* cut 2 days ago */ 
      ('2016-04-20', null),   /* not cut yet */ 
      ('2016-04-23', '2016-04-23'), /* cut yesterday */ 
      ('2016-04-23', null),   /* not cut yet */ 
      ('2016-04-24', '2016-04-24'), /* cut today */ 
      ('2016-04-24', '2016-04-26') /* cut tomorrow (because I can :p) */ 
    ) as laser_sheet(created_at, cut_at) 
) 
SELECT 
    date_serie.day, 
    /* we need to count if any laser_sheet matches this day */ 
    count(laser_sheet.*) as sheets_left_to_cut 
FROM 
    date_serie 
    LEFT JOIN laser_sheet 
    /* notice here your custom join clause */ 
    ON laser_sheet.created_at < date_serie.day 
    AND (
     laser_sheet.cut_at IS NULL 
     OR laser_sheet.cut_at > date_serie.day 
    ) 
GROUP BY 
    date_serie.day 
ORDER BY 
    date_serie.day 
; 

そして、ここでは結果が

  day   | sheets_left_to_cut 
---------------------+-------------------- 
2016-04-17 00:00:00 |     0 
2016-04-18 00:00:00 |     0 
2016-04-19 00:00:00 |     0 
2016-04-20 00:00:00 |     0 
2016-04-21 00:00:00 |     3 
2016-04-22 00:00:00 |     2 
2016-04-23 00:00:00 |     2 
2016-04-24 00:00:00 |     3 
(8 rows) 
です
関連する問題