2012-01-02 3 views
2

У меня есть требование рассчитать суммарную статистику, агрегированную по определенным пользовательским периодам времени. В частности, сеть ресторанов работает 24 часа в сутки. Мне нужно рассчитать статистику, например, общий объем продаж по периодам, где периоды «Завтрак», «Обед», «Ужин» и «Ночь». Для этой компании официальный день, за который они отслеживают статистику, начинается после обеда. Таким образом, 24-часовой период, который составляет официальный день, начинается с 8 вечера и длится до 8 вечера по тихоокеанскому времени) на следующий день. Это один период. Другой период - «Ночь», которая длится с 8 вечера до 5:30 утра. Я ставлю эти определения в таблицу под названием «tdef» как так:Oracle основанная на времени аналитика

drop table tdef cascade constraints 
; 

create table tdef 
(
    cd char(3) not null, 
    start_ts date not null, 
    stop_ts date not null 
) 

А затем вставить определения в таблицу tdef, которые хранятся как даты, когда дата начала всегда начинается с 1 января 1900 года, и если она охватывает через полночь, то она заканчивается на 2 января 1900 года, как это так,

insert into tdef (start_ts, stop_ts, cd) 
values 
(
to_date('1900/01/01 20:00:00', 'yyyy/mm/dd hh24:mi:ss'), 
to_date('1900/01/02 19:59:59', 'yyyy/mm/dd hh24:mi:ss'), 
'24H' 
); 

insert into tdef (start_ts, stop_ts, cd) 
values 
(
to_date('1900/01/01 10:30:00', 'yyyy/mm/dd hh24:mi:ss'), 
to_date('1900/01/01 13:29:59', 'yyyy/mm/dd hh24:mi:ss'), 
'LUN 
); 

insert into tdef (start_ts, stop_ts, cd) 
values 
(
to_date('1900/01/01 15:30:00', 'yyyy/mm/dd hh24:mi:ss'), 
to_date('1900/01/02 08:29:59', 'yyyy/mm/dd hh24:mi:ss'), 
'ON' 
); 

у меня есть очень большой стол (около 2,5 миллиардов строк), который содержит все операции регистра. Мне нужно суммировать продажи по дате (их определение 8 PM-8PM), размер продукта и времени и хранить это в таблице для быстрого доступа к отчетам. Стол должен выглядеть следующим образом:

Dec 12 2011, Hamburger, 24H, 1000 
Dec 12 2011, Hamburger, ON, 100 
Dec 12 2011, Hamburger, LUN, 400 

Вот что я сделал, чтобы достичь этого, я добавил две даты столбцов в таблице транзакций, которые время сделки по 1/1/1900 и 1/2/1900, например:

to_date(concat('01/01/1900 ', tran_tm), 'mm/dd/yyyy hh24:mi'), 
to_date(concat('01/02/1900 ', tran_tm), 'mm/dd/yyyy hh24:mi') 

Я проиндексировал эти две колонки. Затем я создал таблицу перекрестного поиска, в которой связаны идентификаторы транзакций с временными кодами. Каждый код транзакции может быть в течение более чем одного раза исправления. Так это выглядит следующим образом:

24H, 1 
24H, 2 
24H, 3 
... 
LUN, 100 
LUN, 101 
LUN, 102 
... 
ON, 1 
ON, 2 
... 

Я использовал две вставки операторов выбора для достижения этой цели:

select t.trans_id, td.cd, to_date(to_char(to_date(concat(to_char(ts, 'mm/dd/yyyy '), to_char(td.stop_ts, 'hh24:mi:ss')), 'mm/dd/yyyy hh24:mi:ss', 'yyyymmdd'), 'yyyymmdd') 
from trans t, tdef td 
where ts1 >= td.start_ts and ts1 <= td.stop_ts 

select t.trans_id, td.cd, to_date(to_char(to_date(concat(to_char(ts, 'mm/dd/yyyy '), to_char(td.stop_ts, 'hh24:mi:ss')), 'mm/dd/yyyy hh24:mi:ss', 'yyyymmdd'), 'yyyymmdd') 
from trans t, tdef td 
where ts2 >= td.start_ts and ts2 <= td.stop_ts 

Третье поле является «официальной датой». Как это работает, предположим, что транзакция произошла 12/12/2011 20:01, тогда поле ts1 будет 1/1/1900 8:01 PM, а поле ts2 будет 1/2/1900 8:01 PM. В первом запросе это поле присоединяется к cd '24H' и 'ON'. И официальная дата будет рассчитана как 12/13/2011 для «24H» и 12/13/2011 для «ON». Эта транзакция не будет присоединяться к второму запросу, потому что она находится за пределами диапазона дат. Предположим, что транзакция произошла 13.12.2011 13:05. В первом запросе ts1 присоединился бы так: «24H» на дату 12/13/2011, «LUN» на дату 12/13/2011.

После того, как у меня есть эта таблица, легко агрегировать:

select tdef_trans.dt, sum(sales) from trans, tdef_trans where trans.id = tdef_trans.id and tdef_trans.cd = 'LUN' 

Хотя это решение, кажется, работает, я готов держать пари, есть более элегантный способ сделать это. Есть идеи?

+0

Это домашнее задание? – eaolson

+0

Нет. Не домашнее задание. Реальная жизнь. Я немного изменил ситуацию, применив концепцию к ресторану, потому что это индустрия, которую, как мне кажется, большинство людей понимают. На практике данные поступают из онлайн-аукционов, которые, по моему мнению, представляют собой более сложный контекст. –

ответ

2

Если вы пытаетесь сделать хранилище данных (это похоже на него), то вам может быть проще всего создать таблицу, в которой есть каждая секунда дня в ней и в какой период она принадлежит. Это будет только 86400 строк.

Тогда ваш запрос становится относительно простой присоединиться к этому времени измерения

+0

Спасибо. Мне нравится эта идея, и я использовал ее раньше. Но не сразу понятно, как обращаться с «официальным днем», пересекая полуночный барьер, используя этот метод? Или периоды времени, которые пересекают полуночный барьер. –

+0

Временные периоды, пересекающие барьер, не являются проблемой, так как каждая вторая заявляет, в какой период она находится. Итак, второе 86399 находится в «24H», как и второе 0. Что касается вашего «официального» дня, я предлагаю создать измерение даты с помощью ряд за каждый день с разумной эпохи до разумного дальнего будущего (все еще только 365,25 рядов в год). В каждой строке хранить (среди многих других) начальную и конечную временную отметку «официального дня», обеспечивая только одну границу включительно. Присоединение к размеру даты в этом столбце покажет, к какому «официальному дню» относится сделка. – rejj

+0

Спасибо. Поэтому, если я правильно понимаю, вы предлагаете создать таблицу измерения времени из этого sql: 'select to_date ('19900101', 'YYYYMMDD') + numtodsinterval (n, 'day') в качестве" dt ", to_date (concat (to_char (to_date ('19900101', 'YYYYMMDD') + numtodsinterval (n-1, 'day'), 'YYYYMMDD'), '20:00:00'), 'YYYYMMDD HH24: MI: SS') в качестве "start_ts" , to_date (concat (to_char (to_date ('19900101', 'YYYYMMDD') + numtodsinterval (n, 'day'), 'YYYYMMDD'), '19:59:59'), 'YYYYMMDD HH24: MI: SS') как «stop_ts» from (выберите уровень n из двойного соединения по уровню <= 7305) ' –

1

Добавление ввода/вывода для каждой записи в таблице транзакций для отображения второй сделки в бизнес-период кажется высокую цену платить ,Может быть, вы могли бы вместо того, чтобы хранить и поворачивать данные, как показано ниже запрос:

select case 
     when txn_ts - trunc(txn_ts) > numtodsinterval(20, 'hour') 
      then trunc(txn_ts) + 1 
      else trunc(txn_ts)  
     end as business_day, 
     sum (case when ( txn_ts - trunc(txn_ts) > numtodsinterval(20, 'hour') 
         or txn_ts - trunc(txn_ts) < numtodsinterval(5.5, 'hour') 
       then txn_amt else 0 end) as overnight_sales, 
     sum (case when ( txn_ts - trunc(txn_ts) >= numtodsinterval(5.5, 'hour') 
         and txn_ts - trunc(txn_ts) < numtodsinterval(11, 'hour') 
       then txn_amt else 0 end) as breakfast_sales, 
     sum (case when ( txn_ts - trunc(txn_ts) >= numtodsinterval(11, 'hour') 
         and txn_ts - trunc(txn_ts) < numtodsinterval(4, 'hour') 
       then txn_amt else 0 end) as lunch_sales, 
     sum (case when ( txn_ts - trunc(txn_ts) >= numtodsinterval(11, 'hour') 
         and txn_ts - trunc(txn_ts) < numtodsinterval(4, 'hour') 
       then txn_amt else 0 end) as dinner_sales 
    from txn_table 
group by case when txn_ts - trunc(txn_ts) > numtodsinterval(20, 'hour') 
      then trunc(txn_ts) + 1 
      else trunc(txn_ts)  
      end 

Так что для каждого рабочего дня, у вас есть четыре значения, по одному для каждого сегмента рабочего дня. (Я догадываюсь о завтрашнем/обеденном и обеденном/обеденном контрольных точках.) Составление агрегатов с этой таблицы должно быть довольно простым.

См. Creating Histograms with User-Defined Buckets in the Oracle Data Warehousing Guide для других примеров, включая версию без поворота.

+0

Это решение оказалось очень полезным в нашей редизайне.по существу, нет необходимости фиксировать смещение сеанса в таблице. это можно обрабатывать в логике процедур. конечным пользователям все равно, что они хранятся. нет причин для этого. Благодарю. –