SQL 列出一年中每个季度的开始日期和结束日期,对于给定年份的四个季度,分别列出它们的开始日期和结束日期。
SQL 列出一年中每个季度的开始日期和结束日期 问题描述
对于给定年份的四个季度,分别列出它们的开始日期和结束日期。
SQL 列出一年中每个季度的开始日期和结束日期 解决方案
一年有四个季度,因此需要生成 4 行记录。在生成了足够多的行之后,直接调用各个关系数据库管理系统中的日期函数返回每个季度的开始日期和结束日期即可。我们的目标是生成如下所示的结果集,这里以生成当前年份的记录为例。
QTR Q_START Q_END
--- ----------- -----------
1 01-JAN-2005 31-MAR-2005
2 01-APR-2005 30-JUN-2005
3 01-JUL-2005 30-SEP-2005
4 01-OCT-2005 31-DEC-2005
DB2
同时使用 EMP
表和窗口函数 ROW_NUMBER OVER
生成 4 行纪录。除此之外,也可以使用 WITH
子句达到同样目的(正如许多实例的做法一样),甚至也可以借助任何行数不少于 4 行的表。下面的解决方案选择使用 ROW_NUMBER OVER
函数。
1 select quarter(dy-1 day) QTR,
2 dy-3 month Q_start,
3 dy-1 day Q_end
4 from (
5 select (current_date -
6 (dayofyear(current_date)-1) day
7 + (rn*3) month) dy
8 from (
9 select row_number()over() rn
10 from emp
11 fetch first 4 rows only
12 ) x
13 ) y
Oracle
使用 ADD_MONTHS
函数找到每个季度的开始日期和结束日期。使用 ROWNUM
代表每个开始日期和结束日期分别属于哪个季度。下面的解决方案借助 EMP
表生成 4 行记录。
1 select rownum qtr,
2 add_months(trunc(sysdate,'y'),(rownum-1)*3) q_start,
3 add_months(trunc(sysdate,'y'),rownum*3)-1 q_end
4 from emp
5 where rownum <= 4
PostgreSQL
使用 GENERATE_SERIES
函数生成所需的 4 个季度。使用 DATE_TRUNC
函数对每个季度的日期做截断处理,使之仅精确到年份和月份。使用 TO_CHAR
函数计算出每一对开始日期和结束日期分别属于哪个季度。
1 select to_char(dy,'Q') as QTR,
2 date(
3 date_trunc('month',dy)-(2*interval '1 month')
4 ) as Q_start,
5 dy as Q_end
6 from (
7 select date(dy+((rn*3) * interval '1 month'))-1 as dy
8 from (
9 select rn, date(date_trunc('year',current_date)) as dy
10 from generate_series(1,4) gs(rn)
11 ) x
12 ) y
MySQL
使用 T500
表生成 4 行数据(每个季度一行)。使用 DATE_ADD
和 ADDDATE
函数计算出每个季度的开始日期和结束日期。使用 QUARTER
函数计算出每一对开始和结束日期分别属于哪个季度。
1 select quarter(adddate(dy,-1)) QTR,
2 date_add(dy,interval -3 month) Q_start,
3 adddate(dy,-1) Q_end
4 from (
5 select date_add(dy,interval (3*id) month) dy
6 from (
7 select id,
8 adddate(current_date,-dayofyear(current_date)+1) dy
9 from t500
10 where id <= 4
11 ) x
12 ) y
SQL Server
使用 WITH
递归查询生成 4 行数据。使用 DATEADD
函数找出开始日期和结束日期。使用 DATEPART
函数计算出每一对开始日期和结束日期分别属于哪个季度。
1 with x (dy,cnt)
2 as (
3 select dateadd(d,-(datepart(dy,getdate())-1),getdate()),
4 1
5 from t1
6 union all
7 select dateadd(m,3,dy), cnt+1
8 from x
9 where cnt+1 <= 4
10 )
11 select datepart(q,dateadd(d,-1,dy)) QTR,
12 dateadd(m,-3,dy) Q_start,
13 dateadd(d,-1,dy) Q_end
14 from x
15 order by 1
SQL 列出一年中每个季度的开始日期和结束日期 扩展知识
DB2
首先生成 4 行数据(取值为 1 到 4),每个季度一行。内嵌视图 X
使用窗口函数 ROW_NUMBER OVER
和 FETCH FIRST
子句,仅返回 EMP
表的前 4 行数据,结果如下所示。
select row_number()over() rn
from emp
fetch first 4 rows only
RN
--
1
2
3
4
然后找出当前年份的第一天,然后加上 n 个月,n 是 RN
的 3 倍(分别在当前年份第一天的基础上加上 3 个月、6 个月、9 个月和 12 个月),结果如下所示。
select (current_date -
(dayofyear(current_date)-1) day
+ (rn*3) month) dy
from (
select row_number()over() rn
from emp
fetch first 4 rows only
) x
DY
-----------
01-APR-2005
01-JUL-2005
01-OCT-2005
01-JAN-2006
现在,DY
的值就是每个季度最后一天再加上 1 天的日期。下一步要计算出每个季度的开始日期和结束日期。DY
值减去 1 天就是每个季度的结束日期,DY
减去 3 个月就是每个季度的开始日期。针对 DY-1
的值(每个季度的最后一天)调用 QUARTER
函数,计算出每一对开始日期和结束日期分别属于哪个季度。
Oracle
把函数 ROWNUM
、TRUNC
和 ADD_MONTHS
组合起来使用,就能很容易地解决本问题。为找出每个季度的开始日期,只要在当前年份第一天的基础上加上 n 个月即可,n 是 (ROWNUM-1)*3
(计算结果分别是 0、3、6 和 9)。为找出每个季度的结束日期,在当前年份第一天的基础上分别加上 n 个月再减去 1 天,n 等于 ROWNUM*3
。顺便多说一句,做季度相关的计算时,函数 TO_CHAR
和 TRUNC
搭配上格式化选项 q
非常有用。
PostgreSQL
首先调用 DATE_TRUNC
函数截断当前日期,得到当前年份第一天。然后,加上 n 个月再减去 1 天,n 是 RN
(GENERATE_SERIES
函数的返回值)的 3 倍。结果如下所示。
select date(dy+((rn*3) * interval '1 month'))-1 as dy
from (
select rn, date(date_trunc('year',current_date)) as dy
from generate_series(1,4) gs(rn)
) x
DY
-----------
31-MAR-2005
30-JUN-2005
30-SEP-2005
31-DEC-2005
现在得到了每个季度的结束日期,下一步要找出开始日期,用 DY
减去 2 个月,并调用 DATE_TRUNC
函数做截断处理即可。最后,针对每个季度的最后一天(DY
)调用 TO_CHAR
函数,计算出每一对开始日期和结束日期分别属于哪个季度。
MySQL
首先调用 ADDDATE
和 DAYOFYEAR
函数找出当前年份的第一天,然后调用 DATE_ADD
函数在当前年份第一天的基础上加上 n 个月,n 等于 T500.ID
乘以 3。结果如下所示。
select date_add(dy,interval (3*id) month) dy
from (
select id,
adddate(current_date,-dayofyear(current_date)+1) dy
from t500
where id <= 4
) x
DY
-----------
01-APR-2005
01-JUL-2005
01-OCT-2005
01-JAN-2006
现在得到了每个季度最后一天再加上 1 天的日期;为了找出每个季度的结束日期,只要用 DY
值减去 1 天即可。下一步要找出每个季度的开始日期,从 DY
值里减去 3 个月即可。在每个季度结束日期的基础上调用 QUARTER
函数计算出每一对开始日期和结束日期分别属于哪个季度。
SQL Server
首先找出当前年份的第一天,然后调用 DATEADD
函数逐次加上 n 个月,n 是当前迭代次数的 3 倍(共有 4 次迭代,因而分别加上了 3×1 个月,3×2 个月,等等)。结果如下所示。
with x (dy,cnt)
as (
select dateadd(d,-(datepart(dy,getdate())-1),getdate()),
1
from t1
union all
select dateadd(m,3,dy), cnt+1
from x
where cnt+1 <= 4
)
select dy
from x
DY
-----------
01-APR-2005
01-JUL-2005
01-OCT-2005
01-JAN-2006
DY
值是每个季度最后一天再加上 1 天的日期。为得到每个季度的结束日期,只要调用 DATEADD
函数从 DY
里减去 1 天即可。为找出每个季度的开始日期,调用 DATEADD
函数从 DY
里减去 3 个月。借助 DATEPART
函数根据每个季度的结束日期计算出每一对开始日期和结束日期分别属于哪个季度。