Sql计算一年中周内各日期的次数

本文介绍如何在多种数据库环境中计算一年内每周各天的数量,包括DB2、MySQL、Oracle、PostgreSQL和SQL Server等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

计算一年中周内各日期(星期日、星期一 ……星期六)的次数。

解决方案

要计算一年中周内各日期分别有多少个,必须:

1.    生成一年内的所有日期。

2.    设置日期格式,得到每个日期对应为星期几。

3.    计数周内各日期分别有多少个。

DB2

使用递归的WITH子句,以避免对至少包含366行的表进行SELECT。使用函数DAYNAME,确定每个日期为星期几,然后计数周内各日期的次数:

 1  with x (start_date,end_date)

 2  as (

 3  select start_date,

 4         start_date + 1 year end_date

 5    from (

 6  select (current_date -

 7          dayofyear(current_date) day)

 8          +1 day as start_date

 9    from t1   

10         ) tmp

11   union all

12  select start_date + 1 day, end_date

13    from x

14   where start_date + 1 day < end_date

15  )

16  select dayname(start_date),count(*)

17    from x

18   group by dayname(start_date)

MySQL

对表T500进行选择操作,生成的行包含一年的所有日期。使用DATE_FORMAT函数,确定每个日期为星期几,然后计数周内各日期的次数:

 1  select date_format(

 2            date_add(

 3                cast(

 4              concat(year(current_date),'-01-01')

 5                     as date),

 6                     interval t500.id-1 day),

 7                     '%W') day,

 8         count(*)

 9    from t500

10   where t500.id <= datediff(

11                        cast(

12                      concat(year(current_date)+1,'-01-01')

13                             as date),

14                        cast(

15                      concat(year(current_date),'-01-01')

16                             as date))

17  group by date_format(

18              date_add(

19                  cast(

20                concat(year(current_date),'-01-01')

21                       as date),

22                       interval t500.id-1 day),

23                       '%W')

Oracle

对于Oracle9i Database或更高版本,可以使用递归的CONNECT BY子句,返回一年内的所有日期。而对于Oracle8i Database或较早版本,则只需对表T500进行选择操作,就能生成包含一年内所有日期的行。另外,使用TO_CHAR函数,可确定每个日期为星期 几,然后计数周内各日期的次数:

首先是CONNECT BY解决方案:

 1 with x as (

 2 select level lvl

 3   from dual

 4  connect by level <= (

 5    add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y')

 6  )

 7 )

 8 select to_char(trunc(sysdate,'y')+lvl-1,'DAY'), count(*)

 9   from x

10  group by to_char(trunc(sysdate,'y')+lvl-1,'DAY')

接下来是Oracle较早版本的解决方案:

1 select to_char(trunc(sysdate,'y')+rownum-1,'DAY'),

2        count(*)

3   from t500

4  where rownum <= (add_months(trunc(sysdate,'y'),12)

5                   - trunc(sysdate,'y'))

6  group by to_char(trunc(sysdate,'y')+rownum-1,'DAY')

PostgreSQL

使用内置函数GENERATE_SERIES,生成包含一年内所有日期的行。然后使用TO_CHAR函数确定每个日期为星期几,然后计数周内各日期的次数。例如:

 1  select to_char(

 2            cast(

 3      date_trunc('year',current_date)

 4                 as date) + gs.id-1,'DAY'),

 5         count(*)

 6    from generate_series(1,366) gs(id)

 7   where gs.id <= (cast

 8                    ( date_trunc('year',current_date) +

 9                         interval '12 month' as date) -

10  cast(date_trunc('year',current_date)

11                        as date))

12   group by to_char(

13               cast(

14         date_trunc('year',current_date)

15            as date) + gs.id-1,'DAY')

SQL Server

使用递归的WITH子句,以避免对至少包含366行的表进行SELECT。在不支持WITH子句的SQL Server版本中,需要使用基干表,有关内容可以参考Oracle的第二个解决方案。使用函数DAYNAME,可确定每个日期为星期几,然后计数周内各 日期的次数。例如:

 1  with x (start_date,end_date)

 2  as (

 3  select start_date,

 4         dateadd(year,1,start_date) end_date

 5    from (

 6  select cast(

 7         cast(year(getdate(  )) as varchar) + '-01-01'

 8              as datetime) start_date

 9    from t1

10         ) tmp

11  union all

12  select dateadd(day,1,start_date), end_date

13    from x

14   where dateadd(day,1,start_date) < end_date

15  )

16  select datename(dw,start_date),count(*)

17    from x

18   group by datename(dw,start_date)

19 OPTION (MAXRECURSION 366)

讨论

DB2

递归WITH视图X的内联视图TMP将返回当前年份的第一天,如下所示:

select (current_date -

        dayofyear(current_date) day)

        +1 day as start_date

  from t1

START_DATE

-----------

01-JAN-2005

下一步,给START_DATE加1年,这样就有了开始日期和结束日期。想要生成一年内的每一天,就必须知道这两个日期。START_DATE和END_DATE如下所示:

select start_date,

       start_date + 1 year end_date

  from (

select (current_date -

        dayofyear(current_date) day)

        +1 day as start_date

  from t1

       ) tmp

START_DATE  END_DATE

----------- -----------

01-JAN-2005 01-JAN-2006

接下来,递归增加START_DATE,每次加1天,直到它等于END_DATE时为止。下面列出了由递归视图X返回的部分行:

with x (start_date,end_date)

as (

select start_date,

       start_date + 1 year end_date

  from (

select (current_date -

        dayofyear(current_date) day)

        +1 day as start_date

  from t1

       ) tmp

 union all

select start_date + 1 day, end_date

  from x

 where start_date + 1 day < end_date

)

select * from x

START_DATE  END_DATE

----------- -----------

01-JAN-2005 01-JAN-2006

02-JAN-2005 01-JAN-2006

03-JAN-2005 01-JAN-2006

...

29-JAN-2005 01-JAN-2006

30-JAN-2005 01-JAN-2006

31-JAN-2005 01-JAN-2006

...

01-DEC-2005 01-JAN-2006

02-DEC-2005 01-JAN-2006

03-DEC-2005 01-JAN-2006

...

29-DEC-2005 01-JAN-2006

30-DEC-2005 01-JAN-2006

31-DEC-2005 01-JAN-2006

最后一步,对于由递归视图X返回的行,使用函数DAYNAME,然后计数周内各日期的次数。下面列出了其最后结果:

with x (start_date,end_date)

as (

select start_date,

       start_date + 1 year end_date

  from (

select (current_date -

        dayofyear(current_date) day)

        +1 day as start_date

  from t1

       ) tmp

 union all

select start_date + 1 day, end_date

  from x

 where start_date + 1 day < end_date

)

select dayname(start_date),count(*)

  from x

 group by dayname(start_date)

START_DATE   COUNT(*)

---------  ----------

FRIDAY            52

MONDAY            52

SATURDAY          53

SUNDAY            52

THURSDAY          52

TUESDAY           52

WEDNESDAY         52

MySQL

该解决方案对表T500进行选择,以便生成包含一年内所有日期的行。第4行的命令返回当前年份的第一天,其方法是求出函数CURRENT_DATE所返回日期的年份,并添加月份和天(采用MySQL的默认数据格式),其结果如下:

select concat(year(current_date),'-01-01')

  from t1

START_DATE

-----------

01-JAN-2005

得到了当前年份的第一天以后,则可使用DATEADD函数在该日期上分别加上T500.ID中的每个值,以生 成该年份的每一天。使用函数DATE_FORMAT,可返回每个日期是星期几。要从表T500中选择想要的行数,只需计算出当前年份的第一天与下一个年份 的第一天之间的日期差,并返回相应数目的行(365或366个)。下面列出了部分结果:

select date_format(

          date_add(

              cast(

            concat(year(current_date),'-01-01')

                   as date),

                   interval t500.id-1 day),

                   '%W') day

  from t500

 where t500.id <= datediff(

         cast(

                    concat(year(current_date)+1,'-01-01')

                           as date),

                      cast(

                    concat(year(current_date),'-01-01')

                           as date))

DAY

-----------

01-JAN-2005

02-JAN-2005

03-JAN-2005

...

29-JAN-2005

30-JAN-2005

31-JAN-2005

...

01-DEC-2005

02-DEC-2005

03-DEC-2005

...

29-DEC-2005

30-DEC-2005

31-DEC-2005

得到了当前年份的每一天后,则可以计数由函数DAYNAME返回的周内各日期各自的出现次数。最终结果如下所示:

select date_format(

          date_add(

              cast(

            concat(year(current_date),'-01-01')

                   as date),

                   interval t500.id-1 day),

                   '%W') day,

       count(*)

  from t500

 where t500.id <= datediff(

                      cast(

                    concat(year(current_date)+1,'-01-01')

                           as date),

                      cast(

                    concat(year(current_date),'-01-01')

                           as date))

group by date_format(

            date_add(

                cast(

              concat(year(current_date),'-01-01')

                     as date),

                     interval t500.id-1 day),

                     '%W')

DAY       COUNT(*)

--------- ----------

FRIDAY            52

MONDAY            52

SATURDAY          53

SUNDAY            52

THURSDAY          52

TUESDAY           52

WEDNESDAY         52

Oracle

这个解决方案既提供了对表T500(基干表)的选择操作的方法,也提供了使用递归的CONNECT BY和WITH子句的方法,为当前年的每一天生成一行信息。调用函数TRUNC,可以从当前日期截取当前年份的第一天。

如果采用CONNECT BY/WITH解决方案,则可以使用伪列(preudo-column)LEVEL生成起始值为1的序列数。要生成这种解决方案所要求数目的行,需根据当 前年的第一天与下一年的第一天之间相差的天数(365或366个)对ROWNUM或LEVEL进行筛选;然后,通过给当前年的第一天加上ROWNUM或 LEVEL,递增得到一年中的每一天。下面列出了部分结果:

/* Oracle 9i and later */

with x as (

select level lvl

  from dual

 connect by level <= (

   add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y')

 )

)

select trunc(sysdate,'y')+lvl-1

  from x

如果采用基干表解决方案,则可以在其内部使用至少包含366行的任意表或视图。因为Oracle引入了函数ROWNUM,所以表中不需要包含从1开始递增的数值。参阅下面的例子,它使用基干表T500返回当前年的每一天:

/* Oracle 8i and earlier */

select trunc(sysdate,'y')+rownum-1 start_date

  from t500

 where rownum <= (add_months(trunc(sysdate,'y'),12)

                  - trunc(sysdate,'y'))

START_DATE

-----------

01-JAN-2005

02-JAN-2005

03-JAN-2005

...

29-JAN-2005

30-JAN-2005

31-JAN-2005

...

01-DEC-2005

02-DEC-2005

03-DEC-2005

...

29-DEC-2005

30-DEC-2005

31-DEC-2005

无论采用哪种方法,最终都要使用函数TO_CHAR返回每个日期是星期几,并计算各自出现的次数。其结果如下所示:

/* Oracle 9i and later */

with x as (

select level lvl

  from dual

 connect by level <= (

   add_ml> connect by level <= (

   add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y')

 )

)

select to_char(trunc(sysdate,'y')+lvl-1,'DAY'), count(*)

  from x

 group by to_char(trunc(sysdate,'y')+lvl-1,'DAY')

/* Oracle 8i and earlier */

select to_char(trunc(sysdate,'y')+rownum-1,'DAY') start_date,

       count(*)

  from t500

 where rownum <= (add_months(trunc(sysdate,'y'),12)

                  - trunc(sysdate,'y'))

 group by to_char(trunc(sysdate,'y')+rownum-1,'DAY')

START_DATE   COUNT(*)

---------- ----------

FRIDAY             52

MONDAY             52

SATURDAY           53

SUNDAY             52

THURSDAY           52

TUESDAY            52

WEDNESDAY          52

PostgreSQL

首先使用DATE_TRUNC函数返回当前日期的年份(如下所示,对T1 进行选择操作,只返回一行信息):

select cast(

         date_trunc('year',current_date)

       as date) as start_date

  from t1

START_DATE

-----------

01-JAN-2005

然后,对至少包含366行的一个行来源(实际上任何表表达式均可)进行选择操作。本解决方案把函数 GENERATE_SERIES 当作行来源使用,当然,也可以使用表T500。然后在当前年的第一天上加上各行的序号(原文是加1,不妥,译者注),直到返回一年内的每一天(如下所 示):

select cast( date_trunc('year',current_date)

               as date) + gs.id-1 as start_date

  from generate_series (1,366) gs(id)

 where gs.id <= (cast

                  ( date_trunc('year',current_date) +

                       interval '12 month' as date) -

     cast(date_trunc('year',current_date)

                      as date))

START_DATE

-----------

01-JAN-2005

02-JAN-2005

03-JAN-2005

...

29-JAN-2005

30-JAN-2005

31-JAN-2005

...

01-DEC-2005

02-DEC-2005

03-DEC-2005

...

29-DEC-2005

30-DEC-2005

31-DEC-2005

最后,使用函数TO_CHAR返回每个日期是星期几,再计数周内各日期的次数。最终结果如下所示:

select to_char(

          cast(

    date_trunc('year',current_date)

               as date) + gs.id-1,'DAY') as start_dates,

       count(*)

  from generate_series(1,366) gs(id)

 where gs.id <= (cast

                  ( date_trunc('year',current_date) +

                       interval '12 month' as date) -

     cast(date_trunc('year',current_date)

                      as date))

 group by to_char(

             cast(

       date_trunc('year',current_date)

          as date) + gs.id-1,'DAY')

START_DATE   COUNT(*)

---------- ----------

FRIDAY             52

MONDAY             52

SATURDAY           53

SUNDAY             52

THURSDAY           52

TUESDAY            52

WEDNESDAY          52

SQL Server

递归WITH视图X中的内联视图TMP返回当前年份的第一天,如下所示 :

select cast(

       cast(year(getdate(  )) as varchar) + '-01-01'

            as datetime) start_date

  from t1

START_DATE

-----------

01-JAN-2005

在有了当前年份的第一天后,给START_DATE加1年,这样既有开始日期,又有结束日期。要生成一年内的每一天,必须知道这两个值。START_DATE和END_DATE如下所示:

select start_date,

        dateadd(year,1,start_date) end_date

  from (

select cast(

       cast(year(getdate(  )) as varchar) + '-01-01'

            as datetime) start_date

  from t1

       ) tmp

START_DATE  END_DATE

----------- -----------

01-JAN-2005 01-JAN-2006

接下来,递归递增START_DATE值,每次加1,直到它等于END_DATE为止(不包括END_DATE)。下面给出了由递归视图X返回的部分行:

with x (start_date,end_date)

 as (

 select start_date,

        dateadd(year,1,start_date) end_date

   from (

 select cast(

        cast(year(getdate(  )) as varchar) + '-01-01'

             as datetime) start_date

   from t1

        ) tmp

 union all

 select dateadd(day,1,start_date), end_date

   from x

  where dateadd(day,1,start_date) < end_date

 )

 select * from x

 OPTION (MAXRECURSION 366)

START_DATE  END_DATE

----------- -----------

01-JAN-2005 01-JAN-2006

02-JAN-2005 01-JAN-2006

03-JAN-2005 01-JAN-2006

...

29-JAN-2005 01-JAN-2006

30-JAN-2005 01-JAN-2006

31-JAN-2005 01-JAN-2006

...

01-DEC-2005 01-JAN-2006

02-DEC-2005 01-JAN-2006

03-DEC-2005 01-JAN-2006

...

29-DEC-2005 01-JAN-2006

30-DEC-2005 01-JAN-2006

31-DEC-2005 01-JAN-2006

最后,对递归视图X返回的行使用函数DATENAME,并计数周内各日期的出现次数,其结果如下:

with x(start_date,end_date)

 as (

 select start_date,

        dateadd(year,1,start_date) end_date

   from (

 select cast(

        cast(year(getdate(  )) as varchar) + '-01-01'

             as datetime) start_date

   from t1

        ) tmp

 union all

 select dateadd(day,1,start_date), end_date

   from x

  where dateadd(day,1,start_date) < end_date

 )

 select datename(dw,start_date), count(*)

   from x

  group by datename(dw,start_date)

 OPTION (MAXRECURSION 366)

START_DATE   COUNT(*)

---------  ----------

FRIDAY            52

MONDAY            52

SATURDAY          53

SUNDAY            52

THURSDAY          52

TUESDAY           52

WEDNESDAY         52

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值