数据库语法总结(9)——操作日期

今天本章继续更新日期相关的处理,上一张主要讲日期的计算,本章主要讲日期的操作

1、判断当前年份是否是闰年

这个的判断一直是有个热点话题,实现的方法有很多种,咱们主要根据上一章数据库语法总结(8)——计算日期的函数讲一下思路,以Oracle为例:

(1)判断年份是否可以整除4,可以整除就是闰年

SELECT (CASE 
MOD(to_number(to_char(SYSDATE,'yyyy')),4)--MOD为求余函数
WHEN 0 THEN '闰年' ELSE '平年' END) 年 
  FROM dual;

(2)找到当前年份的第二个月,判断最后一天,如果是2月29就是闰年

SELECT to_char(
    last_day(
        add_months(TRUNC(SYSDATE,'y'),1)
    )
,'DD') AS DAYS FROM dual;

2、找出当前年份一共有多少天、月份、星期等

(1)查找当前年份天数,就是找到当前年份的第一天,和下一年的第一天,计算二者之间的间隔,我们上一章相关函数介绍了(同样这里就只列一下Oracle的例子供大家参考)

SELECT add_months(trunc(sysdate,'y'),12)
    -trunc(sysdate,'y') AS days FROM dual;

(2)查找当前月份天数,查找当前月的第一天和下一个月第一天计算间隔

SELECT add_months(trunc(sysdate,'mm'),1)-
    trunc(sysdate,'mm') AS days FROM dual;

(3)查找一年中所有的星期几

例如查找星期五,首先先把当年所有的天数查询出来,然后查找星期五的数据

查询所有天数有多种方式

--借助系统表找出足够的天数长度,但是有风险,万一是闰年那就是366天<367
WITH x AS
(SELECT trunc(sysdate,'y')+rownum-1 dy
	FROM all_tables	WHERE rownum<366
)
--递归的方式返回当年的每一天
WITH x AS 
(
SELECT trunc(sysdate,'y')+LEVEL-1 dy
 FROM dual CONNECT BY LEVEL<=add_months(trunc(sysdate,'y'),12)-trunc(sysdate,'y')
)

--上面二选其一,然后查询对应的星期数
SELECT dy FROM x WHERE to_char(dy,'dy') ='星期五'--星期五要注意英文下是fri

(4)查找一个月中第一个和最后一个星期几

在Oracle中可以直接使用函数next_day和last_day运算 

 select next_day(trunc(sysdate,'mm')-1,'星期一') first_monday,
       next_day(last_day(trunc(sysdate,'mm'))-7,'星期一') last_monday
  from dual;

next_day求指定日期的下一个日期,语法就是next_day(data,weekdate)

对于其他的dbms来说,没有如此方便的函数,所以要运用上面提到的所有函数计算,我发现有些网站写的十分全面,我在这就不写了 

SQL 找出当前月份的第一个和最后一个星期一 (geek-docs.com)

3、创建日历

此处仍以Oracle为例:先使用connect by 返回当月的每一天,然后使用case和max合并一周的各天

  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) Mo,
         max(case dw when 3 then dm end) Tu,
         max(case dw when 4 then dm end) We,
         max(case dw when 5 then dm end) Th,
         max(case dw when 6 then dm end) Fr,
         max(case dw when 7 then dm end) Sa,
         max(case dw when 1 then dm end) Su
    from x
   group by wk
   order by wk;

参考:SQL 生成日历|极客教程 (geek-docs.com)

其他的数据库的思路都是差不多的,我们在这就扩展一下各个数据库的函数,方便大家理解

扩展:DB2:dayofweek(date)返回参数(日期、时间戳、日期字符串、时间戳字符串)在周内的天值(从周日开始1到7)

week_iso(date)返回参数在当年内的周值

PostgreSQL:generate_series(start, stop, step)生成一个数值序列,从start 到 stop,步进为step。step不写默认为1

date_trunc(类型,值)截断日期,与Oracle中的trunc功能类似,用法为date_trunc('month',now())

Mysql:date_format(date,format)用于以不同的格式显示日期/时间数据。

adddate()在指定的日期上进行修改,例如现在的日期加5天

Sqlserver:datepart(type,date)用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等等

4、查找季度的第一天和最后一天

(1)列出一年中各个季度的第一天和最后一天

首先我们知道一共有4个季度,所以要生成四行数据,然后利用我们这两张讲的函数找出每个季度的第一天和下一个季度的第一天-1就可以了

Oracle

select rownum qtr,
        add_months(trunc(sysdate,'y'),(rownum-1)*3) q_start,
        add_months(trunc(sysdate,'y'),rownum*3)-1 q_end
   from T_DEMP_3 
  where rownum <= 4

DB2有返回季度的函数quarter()

   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 T_DEMP_3 
   fetch first 4 rows only
         ) x
         ) y

PostgreSQL使用 GENERATE_SERIES 函数生成所需的 4 个季度。使用 DATE_TRUNC 函数对每个季度的日期做截断处理,使之仅精确到年份和月份。使用 TO_CHAR 函数计算出每一对开始日期和结束日期分别属于哪个季度。

   select to_char(dy,'Q') as QTR,
          date(
            date_trunc('month',dy)-(2*interval '1 month')
          ) as Q_start,
          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使用 T500 表生成 4 行数据(每个季度一行)。使用 DATE_ADD 和 ADDDATE 函数计算出每个季度的开始日期和结束日期。使用 QUARTER 函数计算出每一对开始和结束日期分别属于哪个季度。

   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

SQL Server使用 WITH 递归查询生成 4 行数据。使用 DATEADD 函数找出开始日期和结束日期。使用 DATEPART 函数计算出每一对开始日期和结束日期分别属于哪个季度。

   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 datepart(q,dateadd(d,-1,dy)) QTR,
        dateadd(m,-3,dy) Q_start,
        dateadd(d,-1,dy) Q_end
   from x
  order by 1

(2)确定某个具体年份季度的第一天和最后一天

主要思路是使用substr函数从内嵌视图x里提取出年份,使用mod函数提取出对应的季度序号。Oracle、Mysql、PostgreSQL、DB2除了一些函数不同,写法都是类似的 

  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 20051 yrq from dual union all
  select 20052 yrq from dual union all
  select 20053 yrq from dual union all
  select 20054 yrq from dual
        ) x
        ) y

Sqlserver略有不同,它没有提供对应的mod函数,要使用求模运算符%确定季度

select dateadd(m,-2,q_end) q_start,
       dateadd(d,-1,dateadd(m,1,q_end)) q_end
  from (
select cast(substring(cast(yrq as varchar),1,4)+'-'+
       cast(yrq%10*3 as varchar)+'-1' as datetime) q_end
  from (
select 20051 yrq from t1 union all
select 20052 yrq from t1 union all
select 20053 yrq from t1 union all
select 20054 yrq from t1
       ) x
       ) y

扩展:mod(m,n)
(1)mod返回m除以n的余数,如果n是0,返回m。
(2)这个函数以任何数字数据类型或任何非数值型数据类型为参数,可以隐式地转换为数字数据类型。
Oracle用最高的数字优先级来确定参数,隐式地将剩下的参数转换为该数据类型,并返回数据类型。

5、根据入职时间统计每个月的招聘人员数量

我们需要计算出2020~2023年间,公司每个月招聘人员的数量,但是现在人员表emp的只记录了人员的入职时间hiredate,那要如何统计呢?

我们可以先生成2020~2023年间所有的月份,然后关联人员表emp进行统计,需要注意的是没有招聘员工的月份需要统计的数量为0。以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;

剩余的各种类型的数据库大家请参考SQL 填补缺失的日期 (geek-docs.com)

6、根据日期特定部分进行查找比较统计

还是上面提到的人员表emp和时间hiredate

(1) 根据星期几、月份查找出对应的日期,例如你想要查找2月份和3月份的星期二的数据

Oracle和PostgreSQL:

--中文数据库
select 1 AS a,rtrim(to_char(sysdate,'month'))
   from dual
  where rtrim(to_char(sysdate,'month')) in ('2月','3月')
     or rtrim(to_char(sysdate,'day')) = '星期二'
--英文状态下数据库
select 1 AS a,rtrim(to_char(sysdate,'month'))
   from dual
  where rtrim(to_char(sysdate,'month')) in ('February','March')
     or rtrim(to_char(sysdate,'day')) = 'tuesday'

DB2 和 MySQL:

select ename
  from emp
where monthname(hiredate) in ('February','December')
    or dayname(hiredate) = 'Tuesday'

Sqlserver:

select ename
 from emp
where datename(m,hiredate) in ('February','December')
 or datename(dw,hiredate) = 'Tuesday'

(2)根据月份、星期(不限年份)来查找相同的员工名称并连接成字符串

DB2:使用dayofweek函数返回一个数值表示星期几。使用monthname函数返回月份名称。

select a.ename ||
      ' was hired on the same month and weekday as '||
      b.ename msg
 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:使用to_char函数格式化日期得到筛选条件里的星期值和月份。

select a.ename ||
       ' was hired on the same month and weekday as '||
       b.ename as msg
  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:使用date_format函数格式化日期得到筛选条件里的星期值和月份。

select concat(a.ename,
       ' was hired on the same month and weekday as ',
       b.ename) msg
 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:使用datename函数格式化日期得到筛选条件里的星期值和月份。

select a.ename +
       ' was hired on the same month and weekday as '+
       b.ename msg
  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

我这发现有一位博主总结的很全面,给大家推荐一下,望共勉。(我也是边总结边学习哈) 

https://www.cnblogs.com/baidu-google/articles/4143799.html

好了,今天就先介绍到这了,有问题的地方可以私聊或者留言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值