SQL Cookbook 系列 - 日期操作

  1. 确定一年是否是闰年
  2. 确定一年内的天数
  3. 从日期中提取时间的各部分
  4. 确定某个月的第一天和最后一天
  5. 确定一年内属于周内某天的所有日期
  6. 确定某月内第一个和最后一个周内某天的日期
  7. 创建日历
  8. 列出一年中每个季度的开始日期和结束日期
  9. 确定某个给定季度的开始日期和结束日期
  10. 填充丢失的日期
  11. 按照给定的时间单位进行查找
  12. 使用日期的特殊部分比较记录
  13. 识别重叠的日期范围

1.确定一年是否是闰年

方案:检查2月最后一天是29,确定是否是闰年

db2: with x(dy,mth) as (

select dy, month(dy) from (

select (current_date - dayofyear(current_date) days +1 days)+1 months as dy from t1

) tmp1 union all

select dy+1 days,mth from x where month(dy+1 day)=mth

)

select max(day(dy)) from x;

oracle : select to_char(last_day(add_months(tranc(sysdate,'y'),1)),'DD') from t1;

扫描二维码关注公众号,回复: 2251900 查看本文章

postgresql: select max(to_char(tmp2.dy+x.id,'DD')) as dy from (

select dy,to_char(dy,'MM') as mth from (

select cast(cast(date_tranc('year',current_date) as date)+ interval '1 month' as date) as dy from t1

)tmp1

)tmp2,generate_series(0,29) x(id)

where to_char(tmp2.dy+x.id,'MM') = tmp2.mth;

mysql: select day(last_day(date_add(date_add(date_add(current_date,interval -dayofyear(current_date) day),interval 1 day),interval 1 month))) dy from t1;

sqlserver: with x (dy,mth) as (

select dy,month(dy) from (

select dateadd(mm,1,(getdate()-datepart(dy,getdate()))+1) dy from t1

) tmp1 union all

select dateadd(dd,1,dy),mth from x where month(dateadd(dd,1,dy))=mth

)

select max(day(dy)) from x;

Note:估计会在存储过程中用吧

2.确定一年内的天数

db2: select day((curr_year+1 year)) - days(curr_year) from (

select (current_date -dayofyear(current_date) day +1 day) curr_year from t1

) x;

oracle: select add_months(trunc(sysdate,'y'),12) -tranc(sysdate,'y') from dual;

posgresql: select cast((curr_year + interval '1 year') as date) - curr_year from (

select cast(date_trunc('year',current_date) as date) as curr_year from t1

) x;

sqlserver: select datediff(d,curr_year,dateadd(yy,1,curr_year)) from (

select dateadd(d,-datepart(dy,getdate())+1,getdate()) curr_year from t1

) x;

mysql: select datediff((curr_year + interval 1 year),curr_year) from (

select adddate(current_date,-dayofyear(current_date)+1) curr_year from t1

) x;

Note:我不是太确定这个在数据库中会不会用到

3.从日期中提取时间的各部分

db2 : select hour(current_timestamp) hr, minute(current_timestamp) min,

second(current_timestamp) sec, day(current_timestamp) dy,

month(current_timestamp) mth, year(current_timestamp) yr from t1;

oracle: select to_number(to_char(sysdate,'hh24')) hour,

to_number(to_char(sysdate,'mi')) min,

to_number(to_char(sysdate,'ss')) sec,

to_number(to_char(sysdate,'dd')) day,

to_number(to_char(sysdate,'mm')) mth,

to_number(to_char(sysdate,'yyyy')) year from dual;

postgresql: select to_number(to_char(current_timestamp,'hh24'),'99') as hr,

to_number(to_char(current_timestamp,'mi'),'99') as min,

to_number(to_char(current_timestamp,'ss'),'99') as sec,

to_number(to_char(current_timestamp,'dd'),'99') as day,

to_number(to_char(current_timestamp,'mm'),'99') as mth,

to_number(to_char(current_timestamp,'yyyy'),'99') as yr

from t1;

mysql: select date_format(current_timestamp,'%k') hr,

date_format(current_timestamp,'%l') min,

date_format(current_timestamp,'%s') sec,

date_format(current_timestamp,'%d') dy,

date_format(current_timestamp,‘%m’) mon,

date_format(current_timestamp,'%Y') yr from t1;

sqlserver: select datepart(hour,getdate()) hr, depart(minute,getdate()) min,

datepart(second,getdate()) sec,depart(day,getdate()) dy,

datepart(month,getdate()) mon,depart(year,getdate()) yr from t1;

Note:这个操作是在where条件之前执行,要想改进效率必须先执行where条件,再执行日期的分解操作,用处还可以

4.确定某个月的第一天和最后一天

db2: select (current_date-day(current_date) day +1 day) firstday,

(current_date + 1 month - day(current_date) day) lastday from t1;

oracle: select tranc(sysdate,'mm') firstday,last_day(sysdate) lastday from dual;

postgresql: select firstday,cast(firstday + interval '1 month' -interval '1 day' as date) as lastday

from (select cast(date_trunc('month',current_date) as date) as firstday from t1);

mysql: select date_add(current_date,interval -day(current_date)+1 day) firstday,

last_day(current_date) lastday from t1;

sqlserver: select dateadd(day, -day(getdate())+1,getdate()) firstday,

dateadd(day,-day(getdate()),dateadd(month,1,getdate())) lastday from t1;

Note:这个不是很麻烦,就是其中的函数要清楚

5.确定一年内属于周内某一天的所有日期

db2: with x(dy,yr) as (

select dy,year(dy) yr from (

select (current_date-dayofyear(current_date) days + 1 days) as dy from t1

) tmp1 union all

select dy+1 days , yr from x where year(dy+1 day)=yr

)

select dy from x where dayname(dy)='Friday';

oracle: with x as (

select trunc(sysdate,'y')+level-1 dy from t1

connect by level <= add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y')

)

select * from x where to_char(dy,'dy')='fri';

postgresql: select cast(date_trunc('year',current_date) as date)+x.id as dy from generate_series(

0,(select cast(cast(date_trunc('year',current_date) as date)+ interval '1 years as date') -cast(date_trunc('year',current_date) as date))-1

) x(id) where to_char(cast(date_trunc('year',current_date) as date)+x.id,'dy')='fri';

mysql: select dy from (

select adddate(x.dy,interval t500.id-1 day) dy from (

select dy,year(dy) yr from (

select adddate(adddate(current_date,interval -dayofyear(current_date) day), interval 1 day) dy from t1) tmp1

) x,t500 where year(adddate(x.dy,interval t500.id-1 day))=x.yr

) tmp2 where dayname(dy)='Friday';

sqlserver: with x (dy,yr) as (

select dy,year(dy) yr from (

select getdate()-datepart(dy,getdate())+1 dy from t1

) tmp1 union all

select dateadd(dd,1,dy),yr from x where year(dateadd(dd,1,dy))=yr

)

select x.dy from x where datename(dw,x.dy)='Friday' option (maxrecursion 400);

Note:这个我也不是很确定有没有用,除非是在确定每年交易日期的时候有用。例如查询某一个日期是不是交易日

6.确定某月内第一个和最后一个‘周内某天的日期’

db2: with x (dy,mth,is_monday) as (

select dy,month(dy),case when dayname(dy)='Monday' then 1 else 0 end from (

select (current_date-day(current_date) day +1 day) dy from t1

) tmp1 union all

select (dy+1 day),mth,case when dayname(dy+1 day)='Monday' then 1 else 0 end

from x where month(dy + 1 day)=mth

)

select min(dy) first_monday,max(dy) last_monday from x where is_monday=1;

oracle: select next_day(trunc(sysdate,'mm')-1,'MONDAY') first_monday,

next_day(last_day(trunc(sysdate,‘mm’))-7,'MONDAY') last_monday

from dual;

postgresql: select first_monday,cast to_char(first_monday+28,'mm')

when mth then first_monday+28

else first_monday+21

end as last_monday

from (

select case sign(cast(to_char(dy,'d') as integer)-2)

when 0 then dy

when -1 then dy+abs(cast(to_char(dy,'d') as integer)-2)

when 1 then (7-(cast(to_char(dy,'d') as integer)-2))+dy

end as first_monday,mth from (

select cast(date_trunc('month',currnt_date) as date) as dy,

to_char(currnt_date,'mm') as mth from t1

) x

) y;

mysql: select first_monday,case month(adddate(first_monday,28))

when mth then adddate(first_monday,28)

else adddate(first_monday,21)

end last_monday

from (

select case sign(dayofweek(dy)-2)

when 0 then dy

when -1 then adddate(dy,abs(dayofweek(dy)-2))

when 1 then adddate(dy,(7-(dayofweek(dy)-2)))

end first_monday,mth from (

select adddate(adddate(currnt_date,-day(currnt_date)),1) dy

month(currnt_date) mth from t1

) x

) y;

sqlserver : with x (dy,mth,is_monday) as (

select dy,mth,case when datepart(dw,dy) =2

then 1 else 0 end from (

select dateadd(day,1,dateadd(day,-day(getdate()),getdate())) dy

month(getdate()) mth from t1

) tmp1 union all

select dateadd(day,1,dy),mth,case when datepart(dw,dateadd(day,1,dy))=2

then 1 else 0 end

from x where month(dateadd(day,1,dy))=mth

)

select min(dy) first_monday,max(dy) last_monday from x where is_monday=1;

Note:这个我能想到的是用来计算母亲节或者是父亲节,其他的用途我没想到

7.创建日历

db2:

with x(dy,dm,mth,dw,wk) as (

select (current_date-day(current_date) day + 1 day) dy,

day(current_date-day(current_date) day +1 day) dm,

month(current_date) mth,

dayofweek(current_date-day(current_date) day + 1 day) dw,

week_iso(current_date-day(current_date) day + 1 day) wk

from t1 union all

select dy+1 day,day(dy+1 day),mth,dayofweek(dy+1 day),week_iso(dy+1 day)

from x where month(dy+1 day)=mth

)

select max(case dw when 2 then dm end) as Mo,

max(case dw when 3 then dm end) as Tu,

max(case dw when 4 then dm end) as We,

max(case dw when 5 then dm end) as Th,

max(case dw when 6 then dm end) as Fr,

max(case dw when 7 then dm end) as Sa,

max(case dw when 1 then dm end) as su

from x group by wk order by wk;

oracle:

with x as (

select * from (

select to_char(trunc(sysdate,'mm')+level-1,'iw') wk,

to_char(trunc(sysdate,'mm')+level-1,'dd') dm,

to_number(to_char(trunc(sysdate,'mm')+level-1,'d')) dw,

to_char(trunc(sysdate,'mm')+level-1,'mm') curr_mth,

to_char(sysdate,'mm') mth

from dual connect by level<=31

) where curr_mth=mth

)

select max(case dw when 2 then dm end) as Mo,

max(case dw when 3 then dm end) as Tu,

max(case dw when 4 then dm end) as We,

max(case dw when 5 then dm end) as Th,

max(case dw when 6 then dm end) as Fr,

max(case dw when 7 then dm end) as Sa,

max(case dw when 1 then dm end) as su

from x group by wk order by wk;

postgresql:

select max(case dw when 2 then dm end) as Mo,

max(case dw when 3 then dm end) as Tu,

max(case dw when 4 then dm end) as We,

max(case dw when 5 then dm end) as Th,

max(case dw when 6 then dm end) as Fr,

max(case dw when 7 then dm end) as Sa,

max(case dw when 1 then dm end) as su

from (

select * from (

select cast(date_trunc('month',current_date) as date)+x.id,

to_char(cast(date_trunc('month',current_date) as date)+x.id,'iw') as wk,

to_char(cast(date_trunc('month',current_date) as date)+x.id,'dd') as dm,

cast(to_char(cast(date_trunc('month',current_date) as date)+x.id,'d') as integer) as dw,

to_char(cast(date_trunc('month',current_date) as date)+x.id,'mm') as curr_mth,

to_char(current_date,'mm') as mth

from generate_series(0,31) x(id)

) x where mth=curr_mth

) y group by wk order by wk;

mysql:

select max(case dw when 2 then dm end) as Mo,

max(case dw when 3 then dm end) as Tu,

max(case dw when 4 then dm end) as We,

max(case dw when 5 then dm end) as Th,

max(case dw when 6 then dm end) as Fr,

max(case dw when 7 then dm end) as Sa,

max(case dw when 1 then dm end) as su

from (

select date_format(dy,'%u') wk,

date_format(dy,'%d') dm,

date_format(dy,'%w')+1 dw

from (

select adddate(x.dy,t500.id-1) dy, x.mth from (

select adddate(current_date,-dayofmonth(current_date)+1) dy,

date_format(adddate(current_date,-dayofmonth(current_date)+1),'%m') mth

from t1

) x,t500 where t500.id<=31 and date_format(adddate(x.dy,t500.id-1),'%m')=x.mth

) y

) z group by wk order by wk;

sqlserver:

with x(dy,dm,mth,dw,wk) as (

select dy,day(dy) dm,datepart(m,dy) mth,datepart(dw,dy) dw,

case wgeb datepart(dw,dy)=1 then datepart(ww,dy)-1

else datepart(ww,dy) end wk

from (

select dateadd(day,-day(getdate())+1,getdate()) dy from t1

) x union all

select dateadd(d,1,dy),day(dateadd(d,1,dy)),mth,datepart(dw,dateadd(d,1,dy)),

case when datepart(dw,dateadd(d,1,dy))=1 then datepart(wk,dateadd(d,1,dy))-1

else datepart(wk,dateadd(d,1,dy)) end

from x where datepart(m,dateadd(d,1,dy))=mth

)

select max(case dw when 2 then dm end) as Mo,

max(case dw when 3 then dm end) as Tu,

max(case dw when 4 then dm end) as We,

max(case dw when 5 then dm end) as Th,

max(case dw when 6 then dm end) as Fr,

max(case dw when 7 then dm end) as Sa,

max(case dw when 1 then dm end) as su

from x group by wk order by wk;

Note:在数据库中输出日历,开眼界了,虽然也不知道这个有什么用

8.列出一年中每一个季度的开始日期和结束日期

db2:

select quarter(dy-1 day) QTR,dy-3 month Q_start,dy-1 day Q_end

from (

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

)y;

oracle:

select rownum qtr,

addmonths(trunc(sysdate,'y'),(rownum-1)*3) q_start,

addmonths(trunc(sysdate,'y'),rownum*3)-1 q_end

from emp where rownum<=4;

postgresql:

select to_char(dy,'Q') as QTR,

date(date_trunc('month',dy)-(2*interval '1 month')) dy as Q_end

from (

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

) y ;

mysql:

select quarter(adddate(dy,-1)) QTR,

date_add(dy,interval -3 month) Q_start,

adddate(dy,-1) Q_end from (

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

) y;

sqlserver:

with x (dy,cnt) as (

select adteadd(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 datepart(q,dateadd(d,-1,dy)) QTR,

dateadd(m,-3,dy) Q_start,

date_add(d,-1,dy) Q_end from x order by 1;

Note:这个可用在按照季度统计数据的时候

9.确定某个给定季度的开始日期和结束日期

db2:

select (q_end-2 month) q_start,

(q_end+1 month)-1 day q_end

from (

select date(substr(cast(yrq as char(4)),1,4)

||'-'||

rtrim(cast(mod(yrq,10)*3 as char(2)))

||'-1') q_end from (

select 20181 yrd from t1 union all

select 20182 yrd from t1 union all

select 20183 yrd from t1 union all

select 20184 yrd from t1

) x

) y;

oracle:

select add_months(q_end,-2) q_start,last_day(q_end) q_end from (

select to_date(substr(yrq,1,4) || mod(yrq,10)*3,'yyyymm') q_end from (

select 20181 yrd from dual union all

select 20182 yrd from dual union all

select 20183 yrd from dual union all

select 20184 yrd from dual

) x

) y ;

postgresql:

select date(q_end-(2*interval '1 month')) as q_start,

date(q_end+interval '1 month' - interval '1 day') as q_end

from (

select to_date(substr(yrd,1,4)||mod(yrd,10)*3,'yyyymm') as q_end from (

select 20181 yrd from t1 union all

select 20182 yrd from t1 union all

select 20183 yrd from t1 union all

select 20184 yrd from t1

) x

) y ;

mysql:

select date_add(adddate(q_end,-day(q_end)+1),interval -2 month) q_start,q_end from (

select last_day(str_to_date(concat(substr(yrd,1,4),mod(yrd,10)*3),'%Y%m')) q_end from (

select 20181 yrd from t1 union all

select 20182 yrd from t1 union all

select 20183 yrd from t1 union all

select 20184 yrd from t1

)

)

sqlserver:

select dateadd(m,-2,q_end) q_start.dateadd(d,-1,dateadd(m,1,q_end)) q_end from (

select cast(substring(cast(yrd as varchar),1,4)

+'-'+cast(yrd%10*3 as varchar)

+'-1' as datetime) q_end from (

select 20181 yrd from t1 union all

select 20182 yrd from t1 union all

select 20183 yrd from t1 union all

select 20184 yrd from t1

) x

) y;

Note:标准的查询

10.填充丢失的日期

db2:

with x (start_date,end_date) as (

select (min(hiredate)-dayofyear(min(hiredate)) day +1 day) start_date,

(max(hiredate)-dayofyear(max(hiredate)) day +1 day) +1 year end_date

from emp union all

select start_date+1 month,end_date from x where (start_date+1 month)<end_date

)

select x.start_date mth,count(e.hiredate) num_hired from x

left join emp e on (x.start_date=(e.hiredate-(day(hiredate)-1) day))

group by x.start_date order by 1;

oracle:

with x as (

select add_months(start_date,level-1) start_date from (

select min(trunc(hiredate,'y')) start_date,

add_months(max(trunc(hiredate,'y')),12) end_date

from emp

) connect by level<=months_between(end_date,start_date)

)

select x.start_date MTH,count(e.hiredate) num_hired

from x, emp e where x.start_date=trunc(e.hiredate(+),'mm')

group by x.start_date order by 1;

postgresql:

create view v as

select cast(extract(year from age(last_month,first_month))*12-1 as interger) as mths from (

select cast(date_trunc('year',min(hiredate)) as date) as first_month,

cast(cast(date_trunc('year',max(hiredate)) as date)+ interval '1 year' as date) as last_month

from emp

) x

select y.mth,count(e.hiredate) as num_hired from (

select cast(e.start_date+(x.id*interval '1 month') as date) as mth

from generate_series(0,(select mths from v)) x(id),(

select cast(date_trunc('year',min(hiredate)) as date) as start_date from emp

) e

) y left join emp e on (y.mth=date_trunc('month',e.hiredate)) group by y.mth order by 1;

mysql:

select x.mth,count(e.hiredate) num_hired from (

select date_add(min_hd,interval t500.id-1 month) mth from (

select min_hd,date_add(max_hd,interval 11 month) mth from (

select adddate(min(hiredate),-dayofyear(min(hiredate))+1) min_hd,

adddate(max(hiredate),-dayodyear(max(hiredate))+1) max_hd

from emp

) x

) y,t500 where date_add(min_hd,interval t500.id-1 month)<=max_hd

) z left join emp e on (z.mth=adddate(date_add(last_day(e.hiredate),interval -1 month),1))

group by z.mth order by 1;

sqlserver:

with x (start_date,end_date) as (

select (min(hiredate)-datepart(dy,min(hiredate))+1) start_date,

dateadd(yy,1,(max(hiredate)-datepart(dy,max(hiredate))+1)) end_date

from emp union all

select dateadd(mm,1,start_date),end_date from x

where dateadd(mm,1,start_date)<end_date

)

select x.start_date mth,count(e.hiredate) num_hired

from x left join emp e

on (x.start_date=dateadd(dd,-day(e.hiredate)+1,e.hiredate))

group by x.start_date order by 1;

Note:按照季度统计数据

11.按照给定的时间单位进行查找

db2/mysql:

select ename from emp

where monthname(hiredate) in ('December','February')

or dayname(hiredate)='Tuesday';

oracle/postgresql:

select ename from emp

where rtrim(to_char(hiredate,'month')) in ('December','February')

or rtrim(to_char(hiredate,'day'))='tuesday';

sqlserver:

select ename from emp

where datename(m,hiredate) in ('December','February')

or datename(dw,hiredate) = 'Tuesday';

Note:按照日期的属性来查找

12.使用日期的特殊部分比较记录

db2:

select a.ename,b.ename from emp a,emp b

where (dayofweek(a.hiredate),monthname(a.hiredate))=

(dayofweek(b.hiredate),monthname(b.hiredate))

and a.empno<b.empno order by a.ename;

oracle/postgresql:

select a.ename,b.ename from emp a,emp b

where to_char(a.hiredate,'DMON')=to_char(b.hiredate,'DMON')

and a.empno<b.empno order by a.ename;

mysql:

select a.ename,b.ename from emp a, emp b

where date_format(a.hiredate,'%w%M')=date_format(b.hiredate,'%w%M')

and a.empno<b.empno order by a.ename;

sqlserver:

select a.ename,b.ename from emp a, emp b

where datename(dw,a.hiredate)=datename(dw,b.hiredate)

and datename(m,a.hiredate)=datename(m,b.hiredate)

and a.empno=b.empno order by a.ename;

Note:按照日期的属性来进行比较

13.识别重叠的日期范围

select a.empno,a.ename,b.project_id,a.project_id

from emp_project a,emp_project b

where a.empno=b.empno

and b.project_start>=a.project_start

and b.project_start<=a.project_end

and b.project_id!=a.project_id;

Note:同一张表中的范围比较

猜你喜欢

转载自blog.csdn.net/seacean2000/article/details/81081620