【问题标题】:how to group a date column based on date range in oracleoracle中如何根据日期范围对日期列进行分组
【发布时间】:2016-12-08 06:53:48
【问题描述】:

我有一个表格,其中包含有关产品的反馈。它有反馈类型(正面、负面),它是一个文本列,是 cmets 制作的日期。我需要获得特定时间段内正面、负面反馈的总数。例如,如果日期范围是 30 天,我需要获得 4 周的正面、负面反馈总数,如果日期范围是 6 个月,我需要获得每个月的正面、负面反馈总数。如何根据日期对计数进行分组。

+--------+------+----------+----------+------------- --+--+--+--+ |斯诺 |用户 |评论 |类型 |评论日期 | | | | +--------+------+----------+----------+------------- --+--+--+--+ | 1 |一个 |啊啊啊积极| 2016 年 6 月 22 日 | | | | | 2 |乙 | bbb |积极| 2016 年 6 月 1 日 | | | | | 3 | c | QQ |负| 2016 年 6 月 2 日 | | | | | 4 | d |抄送 |中性 | 2016 年 5 月 3 日 | | | | | 5 |电子|万维网 |积极| 2016 年 4 月 2 日 | | | | | 6 | f |小号 |负| 2015 年 11 月 11 日 | | | | +--------+------+----------+----------+------------- --+--+--+--+

我试过的查询是

SELECT type, to_char(commenteddate,'DD-MM-YYYY'), Count(type) FROM cmets GROUP BY type, to_char(commenteddate,'DD-MM-YYYY');

【问题讨论】:

  • 请显示表架构、示例数据和预期输出,以及您尝试过的内容。谢谢。
  • 我添加了一个示例表和查询。我需要根据日期间隔对类型进行分组。例如,如果日期间隔是 30 天,我需要获得 4 周的正面、负面反馈总数,如果日期范围是 6 个月,我需要获得每个月的正面、负面反馈总数。如何根据日期对计数进行分组。我正在存储过程中尝试此查询。输入是日期间隔
  • 请同时显示此示例数据的预期输出。

标签: stored-procedures oracle11g


【解决方案1】:

这是一个踢罐头......

假设:

  • 您希望能够将分组切换为仅每周或每月
  • 第一个周期的开始将是反馈数据中的第一个日期;间隔将从这个初始日期开始计算
  • 输出将显示反馈值、时间段、计数
  • 时间段不会重叠,因此时间段将为 x -> x + 间隔 - 1 天
  • 一天中的时间并不重要(评论日期的时间总是 00:00:00)

首先,创建一些示例数据(100 行):

drop table product_feedback purge;

create table product_feedback
as
select rownum as slno
, chr(65 + MOD(rownum, 26)) as userid
, lpad(chr(65 + MOD(rownum, 26)), 5, chr(65 + MOD(rownum, 26))) as comments
, trunc(sysdate) + rownum + trunc(dbms_random.value * 10) as commented_date
, case mod(rownum * TRUNC(dbms_random.value * 10), 3) 
       when 0 then 'positive' 
       when 1 then 'negative' 
       when 2 then 'neutral' end as feedback
from dual
connect by level <= 100
;

我的示例数据如下所示:

select *
from product_feedback
;

SLNO    USERID  COMMENTS    COMMENTED_DATE  FEEDBACK
1   B   BBBBB   2016-08-06  neutral
2   C   CCCCC   2016-08-06  negative
3   D   DDDDD   2016-08-14  positive
4   E   EEEEE   2016-08-16  negative
5   F   FFFFF   2016-08-09  negative
6   G   GGGGG   2016-08-14  positive
7   H   HHHHH   2016-08-17  positive
8   I   IIIII   2016-08-18  positive
9   J   JJJJJ   2016-08-12  positive
10  K   KKKKK   2016-08-15  neutral
11  L   LLLLL   2016-08-23  neutral
12  M   MMMMM   2016-08-19  positive
13  N   NNNNN   2016-08-16  neutral
...

现在是有趣的部分。这是要点:

  • 找出数据中最早和最新的评论日期是什么
  • 包含一个查询,您可以在其中设置时间段(为“WEEKS”或“MONTHS”)
  • 生成最小/最大日期之间的所有(每周或每月)时间段
  • 使用外部连接将产品反馈加入时间段(开始和结束之间的注释日期),以防您想查看所有时间段是否有任何反馈
  • 按反馈、周期开始和周期结束对连接的结果进行分组,并设置一列来计算 3 个可能的反馈值之一

x

with 
min_max_dates -- get earliest and latest feedback dates
as
(select min(commented_date) min_date, max(commented_date) max_date
from product_feedback
)
, time_period_interval
as
(select 'MONTHS' as tp_interval -- set the interval/time period here
   from dual
)
, -- generate all time periods between the start date and end date
time_periods (start_of_period, end_of_period, max_date, time_period) -- recursive with clause - fun stuff!
as
(select mmd.min_date as start_of_period
      , CASE WHEN tpi.tp_interval = 'WEEKS' 
             THEN mmd.min_date + 7
             WHEN tpi.tp_interval = 'MONTHS' 
             THEN ADD_MONTHS(mmd.min_date, 1)
             ELSE NULL
        END - 1 as end_of_period
, mmd.max_date
, tpi.tp_interval as time_period
   from time_period_interval tpi
        cross join
        min_max_dates mmd
UNION ALL
select CASE WHEN time_period = 'WEEKS' 
             THEN start_of_period + 7 * (ROWNUM )
             WHEN time_period = 'MONTHS' 
             THEN ADD_MONTHS(start_of_period, ROWNUM)
             ELSE NULL
        END as start_of_period
, CASE WHEN time_period = 'WEEKS' 
             THEN start_of_period + 7 * (ROWNUM + 1)
             WHEN time_period = 'MONTHS' 
             THEN ADD_MONTHS(start_of_period, ROWNUM + 1)
             ELSE NULL
        END - 1 as end_of_period
, max_date
, time_period
from time_periods
where end_of_period <= max_date 
)
-- now put it all together
select pf.feedback
     , tp.start_of_period
     , tp.end_of_period
     , count(*) as feedback_count
from time_periods tp
left outer join
product_feedback pf
on pf.commented_date between tp.start_of_period and tp.end_of_period
group by tp.start_of_period
       , tp.end_of_period
       , pf.feedback
order by pf.feedback
       , tp.start_of_period
;

输出:

negative    2016-08-06  2016-09-05  6
negative    2016-09-06  2016-10-05  7
negative    2016-10-06  2016-11-05  8
negative    2016-11-06  2016-12-05  1
neutral     2016-08-06  2016-09-05  6
neutral     2016-09-06  2016-10-05  5
neutral     2016-10-06  2016-11-05  11
neutral     2016-11-06  2016-12-05  2
positive    2016-08-06  2016-09-05  17
positive    2016-09-06  2016-10-05  16
positive    2016-10-06  2016-11-05  15
positive    2016-11-06  2016-12-05  6

-- 编辑--

新的和改进的,都在一个易于使用的程序中。 (我将假设您可以配置该过程以以您需要的任何方式使用查询。)我做了一些更改以简化 CASE 几个地方的语句,并注意无论出于何种原因在主 SELECT 中使用 LEFT OUTER JOIN导致我出现 ORA-600 错误,因此我将其切换为 INNER JOIN。

CREATE OR REPLACE PROCEDURE feedback_counts(p_days_chosen IN NUMBER, p_cursor OUT SYS_REFCURSOR)
AS
BEGIN

OPEN p_cursor FOR
    with 
    min_max_dates -- get earliest and latest feedback dates
    as
    (select min(commented_date) min_date, max(commented_date) max_date
    from product_feedback
    )
    , time_period_interval
    as
    (select CASE 
             WHEN p_days_chosen BETWEEN 1 AND 10 THEN 'DAYS'
             WHEN p_days_chosen > 10 AND p_days_chosen <=31 THEN 'WEEKS'
             WHEN p_days_chosen > 31 AND p_days_chosen <= 365 THEN 'MONTHS'
             ELSE '3-MONTHS'
        END as tp_interval -- set the interval/time period here
        from dual --(SELECT p_days_chosen as days_chosen from dual)
    )    
    , -- generate all time periods between the start date and end date
    time_periods (start_of_period, end_of_period, max_date, tp_interval) -- recursive with clause - fun stuff!
    as
    (select mmd.min_date as start_of_period
          , CASE tpi.tp_interval 
                 WHEN 'DAYS'
                 THEN mmd.min_date + 1
                 WHEN 'WEEKS' 
                 THEN mmd.min_date + 7
                 WHEN 'MONTHS' 
                 THEN mmd.min_date + 30
                 WHEN '3-MONTHS'
                 THEN mmd.min_date + 90
                 ELSE NULL
            END - 1 as end_of_period
    , mmd.max_date
    , tpi.tp_interval
       from time_period_interval tpi
            cross join
            min_max_dates mmd
    UNION ALL
    select CASE tp_interval
                 WHEN 'DAYS'
                 THEN start_of_period + 1 * ROWNUM
                 WHEN 'WEEKS' 
                 THEN start_of_period + 7 * ROWNUM 
                 WHEN 'MONTHS' 
                 THEN start_of_period + 30 * ROWNUM
                 WHEN '3-MONTHS'
                 THEN start_of_period + 90 * ROWNUM
                 ELSE NULL
            END as start_of_period
        , start_of_period
          + CASE tp_interval 
                 WHEN 'DAYS'
                 THEN 1
                 WHEN 'WEEKS' 
                 THEN 7
                 WHEN 'MONTHS' 
                 THEN 30
                 WHEN '3-MONTHS'
                 THEN 90
                 ELSE NULL
            END * (ROWNUM + 1) 
            - 1 as end_of_period
    , max_date
    , tp_interval
    from time_periods
    where end_of_period <= max_date 
    )
    -- now put it all together
    select pf.feedback
         , tp.start_of_period
         , tp.end_of_period
         , count(*) as feedback_count
    from time_periods tp
    inner join -- currently a bug that prevents the procedure from compiling with a LEFT OUTER JOIN
    product_feedback pf
    on pf.commented_date between tp.start_of_period and tp.end_of_period
    group by tp.start_of_period
           , tp.end_of_period
           , pf.feedback
    order by tp.start_of_period
           , pf.feedback
    ;
END; 

测试过程(在 SQLPlus 或 SQL Developer 中):

var x refcursor
exec feedback_counts(10, :x)
print :x

【讨论】:

  • 如果选择的天数少于 10 天,我想分组,我需要获取每天的评论数。如果选择的天数大于10天小于31天,需要分组7天。如果选择的天数大于一个月小于一年,需要分组30天,大于一年需要分组 90 天
  • 天数怎么选?在哪里?通过什么机制?
  • 选择的天数作为数字发送到存储过程。我需要知道如何修改查询,
  • 我已经编辑了答案以在过程中显示查询,并根据您上面的评论添加了更多时间段。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-13
  • 2013-01-28
  • 2023-01-28
  • 2016-01-09
相关资源
最近更新 更多