2011-06-01 51 views
7

我有一个events表,其中有两列eventkey(唯一主键)和createtime,它将事件的创建时间存储为NUMBER列中1970年1月1日以来的毫秒数。在Oracle中创建直方图/频率分布的最佳方法?

我想创建一个“直方图”或频率分布,显示在过去一周的每个小时内创建了多少个事件。

这是使用width_bucket()函数在Oracle中编写此类查询的最佳方法吗?是否有可能使用其他Oracle分析函数之一推导出落入每个存储桶的行数,而不是使用width_bucket来确定每行所属的存储分区数量,并在此基础上执行count(*)

-- 1305504000000 = 5/16/2011 12:00am GMT 
-- 1306108800000 = 5/23/2011 12:00am GMT 
select 
timestamp '1970-01-01 00:00:00' + numtodsinterval((1305504000000/1000 + (bucket * 60 * 60)), 'second') period_start, 
numevents 
from (
    select bucket, count(*) as events from (
    select eventkey, createtime, 
    width_bucket(createtime, 1305504000000, 1306108800000, 24 * 7) bucket 
    from events 
    where createtime between 1305504000000 and 1306108800000 
) group by bucket 
) 
order by period_start 

回答

10

如果您createtime是一个日期列,这将是微不足道的:

SELECT TO_CHAR(CREATE_TIME, 'DAY:HH24'), COUNT(*) 
    FROM EVENTS 
GROUP BY TO_CHAR(CREATE_TIME, 'DAY:HH24'); 

因为它是,铸造createtime列不是太难:

select TO_CHAR( 
     TO_DATE('19700101', 'YYYYMMDD') + createtime/86400000), 
     'DAY:HH24') AS BUCKET, COUNT(*) 
    FROM EVENTS 
    WHERE createtime between 1305504000000 and 1306108800000 
group by TO_CHAR( 
     TO_DATE('19700101', 'YYYYMMDD') + createtime/86400000), 
     'DAY:HH24') 
order by 1 

如果您正在寻找fencepost值(例如,我从哪里开始第一个十分位数(0-10% )到下(11-20%),你会做这样的事情:

select min(createtime) over (partition by decile) as decile_start, 
     max(createtime) over (partition by decile) as decile_end, 
     decile 
    from (select createtime, 
       ntile (10) over (order by createtime asc) as decile 
      from events 
     where createtime between 1305504000000 and 1306108800000 
     ) 
+0

这个效果很好,谢谢。不知道为什么我没有想到简单地截断日期,我想我很想搞清楚如何解析和投射这种奇怪的“日期”格式 – 2011-06-01 14:26:37

+0

有没有办法维护create_times的行零计数? – 2014-10-29 15:59:03

3

我不熟悉Oracle的日期函数,但我敢肯定有写这篇声明的Postgres的等效方式:

select date_trunc('hour', stamp), count(*) 
from your_data 
group by date_trunc('hour', stamp) 
order by date_trunc('hour', stamp) 
+1

在PG中完美工作!真的太快了。 – 2016-01-17 16:56:31

1

差不多亚当相同的反应,但我宁愿如果需要保持period_start为时间字段,以便更容易进一步筛选:

with 
events as 
(
    select rownum eventkey, round(dbms_random.value(1305504000000, 1306108800000)) createtime 
    from dual 
    connect by level <= 1000 
) 
select 
    trunc(timestamp '1970-01-01 00:00:00' + numtodsinterval(createtime/1000, 'second'), 'HH') period_start, 
    count(*) numevents 
from 
    events 
where 
    createtime between 1305504000000 and 1306108800000 
group by 
    trunc(timestamp '1970-01-01 00:00:00' + numtodsinterval(createtime/1000, 'second'), 'HH') 
order by 
    period_start 
+0

你能解释一下事件作为()的目的吗?为什么你选择随机值?我不熟悉Oracle语法 – 2011-06-01 14:06:14

+0

抱歉...由于我没有数据表来运行查询,因此我正在生成随机数据来模拟您的表中可能存在的内容。 “with events”语句只允许我将该查询别名为“事件”,这样查询的其余部分将与您可以直接使用的事件表相匹配,而无需进行任何更改。为了您的目的,只需删除上面的所有内容“select trunc(....” – Craig 2011-06-01 14:23:47

+0

啊谢谢,我明白这对于这种类型的回答会有用:) – 2011-06-01 14:26:04