开发者

How to “group by” over a DATETIME range?

I'm trying to bulid up a datetime range based transactions report, for a business that can be open across two days, depending on the shift management.

The user can select a datetime range (monthly, daily, weekly, freely...), the query I implemented get the startDateTime and the EndDateTime, and will return all the transactions total grouped by day.

I.E.

DateTime       Total Sales
---------------------------
10/15/2010      $2,300.38
10/16/2010      $1,780.00
10/17/2010      $4,200.22
10/20/2010      $900.66

My problem is that if the shift of the business is setted, for example, from 05.00 AM to 02.00 AM of the next day, all the transactions done from midnight to 02.00 AM will be grouped in the next day... and so on... the totals are corrupted. When a business has a shift like this, it wants a report based on that shift, but without code patching (I'm using Java calling Oracle native queries), I'm unable to get the requ开发者_Python百科ested report.

I'm wondering if there is some smart manner to group by a datetime range these sets of transactions using nothing more than Oracle.

Here goes the query, for the the month of July:

SELECT Q1.dateFormat, NVL(Q1.sales, 0) 
    FROM (
        SELECT to_date(to_char(tx.datetimeGMT +1/24 , 'mm-dd-yyyy'), 'mm-dd-yyyy') AS dateFormat                    
                , NVL(SUM(tx.amount),0) AS sales
            FROM Transaction tx
            WHERE tx.datetimeGMT > to_date('20100801 08:59:59', 'yyyymmdd hh24:mi:ss') +1/24  
                AND tx.datetimeGMT < to_date('20100901 09:00:00', 'yyyymmdd hh24:mi:ss') + 1/24  
            GROUP BY to_date(to_char(tx.datetimeGMT +1/24 , 'mm-dd-yyyy'), 'mm-dd-yyyy') 
    ) Q1 
    ORDER BY 1 DESC


Thank you all for your answers, by taking a look to them I could write down the query I was searching for:

SELECT CASE 
     WHEN EXTRACT(HOUR FROM TX.DATETIME) >= 5 THEN TO_CHAR(TX.DATETIME,'DD-MM-YYYY')
     WHEN EXTRACT(HOUR FROM TX.DATETIME) BETWEEN 0 AND 2 THEN TO_CHAR(TX.DATETIME-1,'DD-MM-YYYY')
     WHEN EXTRACT(hour from tx.datetime) between 2 and 5 THEN to_char(TX.DATETIME-1,'DD-MM-YYYY')
   END AS age, 
   NVL(SUM(tx.amount),0) AS sales
FROM TRANSACTION TX
WHERE tx.datetime > to_date('20100801 08:59:59', 'yyyymmdd hh24:mi:ss') 
  AND TX.DATETIME < TO_DATE('20100901 09:00:00', 'yyyymmdd hh24:mi:ss')
GROUP BY CASE 
     WHEN EXTRACT(HOUR FROM TX.DATETIME) >= 5 THEN TO_CHAR(TX.DATETIME,'DD-MM-YYYY')
     WHEN EXTRACT(HOUR FROM TX.DATETIME) BETWEEN 0 AND 2 THEN TO_CHAR(TX.DATETIME-1,'DD-MM-YYYY')
     WHEN EXTRACT(hour from tx.datetime) between 2 and 5 THEN to_char(TX.DATETIME-1,'DD-MM-YYYY')
   END 
ORDER BY 1


To group by a date range, you'll have to have this range into a column value into a subquery, and group by it in your query. Obviously, this date range within this column value will be of VARCHAR type.


If the first shift of the day starts at 08:00, and the last shift of that same day ends 07:59 the next day, you can use something like this to group the transactions by the shift date.

select trunc(trans_date - interval '8' hour) as shift_date
      ,sum(amount)
  from transactions
 group 
    by trunc(trans_date - interval '8' hour)
 order 
    by shift_date desc;


You can try this approach (just out of my head, not even sure if it runs):

select
trans_date,
trans_shift,
aggregates(whatever)
from (
    select 
    -- we want to group by normalized transaction date, 
    -- not by real transaction date
    normalized_trans_date,
    -- get the shift to group by
    case 
      when trans_date between trunc(normalized_trans_date) + shift_1_start_offset and 
                              trunc(normalized_trans_date) + shift_1_end_offset then
        1
      when trans_date between trunc(normalized_trans_date) + shift_2_start_offset and 
                              trunc(normalized_trans_date) + shift_2_end_offset then
        2
      ...
      when trans_date between trunc(normalized_trans_date) + shift_N_start_offset and 
                              trunc(normalized_trans_date) + shift_N_end_offset then
        N
    end trans_shift,
    whatever
    from (
        select
        -- get a normalized transaction date: if date is before 1st shift
        -- it belongs to the day before
        case 
          when trans_date - trunc(trans_date) < shift_1_start_offset then
            trans_date - 1
          else
            trans_date
        end normalized_trans_date,
        t.*
        from
        transactions t
    )
)
group by trans_date, trans_shift


Ronnis solution with the trunc(trans_date - interval '8' hour) helped me in a similar query. Did a Backup Report and had to summarize output-bytes from RC_BACKUP_SET_DETAILS. The backup task runs for more than 8 hours, there are several RC_BACKUP_SET_DETAILS rows for one job which starts at night time and end the next day.

select  trunc(start_time - interval '12' hour) "Start Date",
   to_char(sum(output_bytes)/(1024*1024*1024),'999,990.0') "Output GB" 
   from rc_backup_set_details  
   where db_key = 173916 and backup_type = 'I' and incremental_level = 0 
   group by trunc(start_time - interval '12' hour) 
   order by 1 asc; 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜