如何根据sql中组中的日期集获取前7天的数据

Posted

技术标签:

【中文标题】如何根据sql中组中的日期集获取前7天的数据【英文标题】:How to get data for previous 7 days based on set of dates in groups in sql 【发布时间】:2016-10-16 04:14:34 【问题描述】:

我正在通过这个论坛进行查询以获取前 7 天的数据,但大多数人都提供当前日期的数据。以下是我的要求:

我有一个表1如下:

这些是每周的开始日期,即星期一

from_date
2016-01-04
2016-01-11
2016-01-18

表 2

从星期一开始,我一周中的所有日子都在这里。例如:1 月 4 日 - 星期一到 1 月 10 日 - 星期日,其他星期也是如此。

get_date    flag    value
2016-01-04  N   4
2016-01-05  N   9
2016-01-06  Y   2
2016-01-07  Y   13
2016-01-08  Y   7
2016-01-09  Y   8
2016-01-10  Y   8
2016-01-11  Y   1
2016-01-12  Y   9
2016-01-13  N   8
2016-01-14  N   24
2016-01-15  N   8
2016-01-16  Y   4
2016-01-17  Y   5
2016-01-18  Y   9
2016-01-19  Y   2
2016-01-20  Y   8
2016-01-21  Y   4
2016-01-22  N   9
2016-01-23  N   87
2016-01-24  Y   3

预期结果 这里 wk 分别是每个开始结束日期的唯一编号 avg value 是该周日期值的平均值。

一周的最后 2 天是周末。

说 2016-01-09 和 2016-01-10 是周末

from_date   get_date    Wk  Total_days  Total_weekdays_flag_Y   Total_weekenddays_flag_Y    Avg_value
2016-01-04  2016-01-10  1   7   3   2   6.714285714
2016-01-11  2016-01-17  2   7   2   2   8.428571429
2016-01-18  2016-01-24  3   7   4   1   17.42857143

谁能帮我解决这个问题,因为我不擅长 sql。

谢谢

【问题讨论】:

你用的是什么数据库,什么版本? 【参考方案1】:
select 
 from_date
, Wk
, count(case when day_of_week <=5 and flag = 'Y' then 1 end) as Total_weekdays_flag_Y   
, count(case when day_of_week > 5 and flag = 'Y' then 1 end) as Total_weekenddays_flag_Y
, avg(value) as Avg_value
from (
 select trunc(get_date,'IW') as from_date
       , (trunc(get_date,'IW')- trunc(date'2016-01-04','IW'))/7 + 1 as Wk
       , flag    
       , value
       , get_date - trunc(get_date,'IW') as day_of_week
       from Table_2)
group by from_date, Wk
order by from_date, Wk;

编辑:

 /*generate some test_data for table 2*/
 with table_2 (get_date, flag, value) as ( 
 select date'2016-01-03' + level, 
 DECODE(mod(level,3),0,'Y','N'), 
 round(dbms_random.value(0,10)) 
 from dual connect by level < 101 
 ), 
 /*generate some test_weeks for table 1*/
 table_1 (FROM_date) as (select date'2016-01-04' + (level-1)*7 from dual connect by level < 101 ) 
 /*main query */
   select 
  from_date 
 , Wk 
 , count(day_of_week) as total 
 , count(case when day_of_week <=5 and flag = 'Y' then 1 end) as      Total_weekdays_flag_Y   
 , count(case when day_of_week > 5 and flag = 'Y' then 1 end) as      Total_weekenddays_flag_Y 
 , avg(value) as Avg_value 
 from ( 
  select last_value(from_date ignore nulls) over (order by get_date) as from_date 
        ,last_value(Wk ignore nulls) over (order by get_date) as Wk 
        , flag     
        , value 
        , get_date - trunc(get_date,'IW') as day_of_week 
        from Table_2 t2 
        full join (select row_number() over (order by from_date) as wk,from_date from table_1) t1 on t2.get_date = t1.from_date 
   ) 
 group by from_date, Wk 
 having count(day_of_week) > 0 
  order by from_date, Wk

【讨论】:

嗨迈克尔...我不能硬编码 2016-01-04,因为它可能会根据表中的内容而有所不同..我们可以假设最小日期。还有一件要考虑的事情是它是否如果一周的开始在一个月的 30 号左右并溢出到下个月,可以处理吗? 嗨,麦迪凯门。我没有得到你想要的。 2016-01-04 - 是星期一,之后的每 7 天都是星期一。可能您不是按常识按周分组,而是按表 1 中的间隔对您的表格进行分组?但不清楚在这种情况下什么是周末。你能解释一下吗 我添加了计算周期的案例 我的意思是每次它可能不会从 2016-01-04 开始。它可以是之前的任何一个星期一,也可以是 2015-12-28 等,而周末总是星期日。【参考方案2】:

在下面的查询中,我在查询中创建了测试数据;在最终形式中,您将删除子查询 table_1table_2 并使用其余部分。

该语法将从 Oracle 11.2 开始工作。在 Oracle 11.1 中,您需要将分解子查询中的列名移动到 select... from dual 部分。或者,由于您实际上只有一个子查询 (prep) 和一个外部查询,因此您可以将 prep 编写为实际的内联子查询。

你的算术在第一周的平均水平似乎有所偏差。

在您的示例输出中,您使用get_date 表示一周的最后一天。这很奇怪,因为在table_2 中,该名称具有不同的含义。我在输出中使用了to_date。我也没有显示total_days - 那总是7,那么为什么要包括它呢? (如果不总是 7,那么有些事情你没有告诉我们;无论如何,count(...),如果应该是这样,很容易添加)。

with
-- begin test data, can be removed in final solution
     table_1 ( from_date ) as (
       select date '2016-01-04' from dual union all
       select date '2016-01-11' from dual union all
       select date '2016-01-18' from dual
     )
     ,
     table_2 ( get_date, flag, value ) as (
       select date '2016-01-04', 'N',  4 from dual union all
       select date '2016-01-05', 'N',  9 from dual union all
       select date '2016-01-06', 'Y',  2 from dual union all
       select date '2016-01-07', 'Y', 13 from dual union all
       select date '2016-01-08', 'Y',  7 from dual union all
       select date '2016-01-09', 'Y',  8 from dual union all
       select date '2016-01-10', 'Y',  8 from dual union all
       select date '2016-01-11', 'Y',  1 from dual union all
       select date '2016-01-12', 'Y',  9 from dual union all
       select date '2016-01-13', 'N',  8 from dual union all
       select date '2016-01-14', 'N', 24 from dual union all
       select date '2016-01-15', 'N',  8 from dual union all
       select date '2016-01-16', 'Y',  4 from dual union all
       select date '2016-01-17', 'Y',  5 from dual union all
       select date '2016-01-18', 'Y',  9 from dual union all
       select date '2016-01-19', 'Y',  2 from dual union all
       select date '2016-01-20', 'Y',  8 from dual union all
       select date '2016-01-21', 'Y',  4 from dual union all
       select date '2016-01-22', 'N',  9 from dual union all
       select date '2016-01-23', 'N', 87 from dual union all
       select date '2016-01-24', 'Y',  3 from dual
     ),
-- end test data, continue actual query
     prep ( get_date, flag, value, from_date, wd_flag ) as (
       select t2.get_date, t2.flag, t2.value, t1.from_date,
              case when t2.get_date - t1.from_date <= 4 then 'wd' else 'we' end
       from   table_1 t1 inner join table_2 t2
                         on t2.get_date between t1.from_date and t1.from_date + 6
     )
select from_date,
       from_date + 6 as to_date,
       row_number() over (order by from_date) as wk,
       count(case when flag = 'Y' and wd_flag = 'wd' then 1 end)
                     as total_weekday_Y,
       count(case when flag = 'Y' and wd_flag = 'we' then 1 end)
                     as total_weekend_Y,
       round(avg(value), 6) as avg_value
from   prep
group by from_date;

输出

FROM_DATE  TO_DATE      WK TOTAL_WEEKDAY_Y TOTAL_WEEKEND_Y  AVG_VALUE
---------- ---------- ---- --------------- --------------- ----------
2016-01-04 2016-01-10    1               3               2   7.285714
2016-01-11 2016-01-17    2               2               2   8.428571
2016-01-18 2016-01-24    3               4               1  17.428571

【讨论】:

HI mathguy..感谢这个解决方案..它有效...我只是在想..如果一周中的某一天从 30 日或 31 日开始,假设会有一个..虽然不确定 @maddykemen - 解决方案应该可以正常工作 - 除非“月”、“年”、“周从一个月开始但在另一个结束”等有特殊含义。这是我的解决方案:它需要table_1 中的每个日期(您有责任确保它们都是星期一,并且您需要在最终报告中包含所有星期一)。然后对于这些日期中的每一个,它将获取table_2 中该日期和该日期 + 6(同一周的星期日)之间的所有行。 (下续) 计算不关心这些日期是一个月还是一年 - 它们在所有情况下都可以正常工作。无需调整。

以上是关于如何根据sql中组中的日期集获取前7天的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用窗口函数获取每个日期值的今天、过去 7 天、过去 30 天的指标?

sqlserver如何根据当前日期获得上月某一天的日期

Oracle SQL:查找指定日期前 7 天的日期,不包括星期日和其他特定日期

PHP+mysql 查询 今天,昨天,最近7天的数据?

SQL语句 如何取得指定月份的最后一天的日期

如何在 SQL Server 中选择最近 7 天的日期