SQL使用技巧(4.2)Greenplum和PostgreSQL日期时间函数

专题:SQL使用技巧——实践是检验SQL函数的唯一标准

  • 一.常规计算函数说明
    • 1.1系统当前日期时间
    • 1.2日期时间格式转换
    • 1.3日期加减计算函数
    • 1.4日期时间部分提取
    • 1.5日期时间函数的嵌套使用
  • 二.使用案例
    • 2.1存储过程计算日期时间表
    • 2.2未完待续

常用的格式化(format)标识符:

yyyy:四位年份
mm:月份,不足两位补0
dd:日期,不足两位补0
hh24:小时,24小时制,不足两位补0
hh12:小时,12小时制,不足两位补0,后面带 am pm
mi:分钟,不足两位补0
ss:秒,不足两位补0

示例:2019/11/21 05:00:00 pm  -- 格式 'yyyy/mm/dd hh12:mi:ss am'
2019-11-21 17:00:00  -- 格式  'yyyy-mm-dd hh24:mi:ss'

一.常规计算函数说明

本章节每一行代码后都有运算说明和执行结果样例,例如 - - 返回当前系统日期 yyyy-mm-dd, 【2023-04-08】

1.1系统当前日期时间

select 
	current_date -- 返回当前系统日期,格式为yyyy-mm-dd 【2023-04-08】
	,current_time -- 返回当前系统时间,格式为hh24:mi:ss 【14:48:32】
	,current_timestamp -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【2023-04-08 14:48:32】
	,now() -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【2023-04-08 14:48:32】
	,localtimestamp -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【2023-04-08 14:48:32】
	,localtime -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【14:48:32】
	;

在这里插入图片描述

1.2日期时间格式转换

to_char()to_timestamp()函数的使用对于日期的结果变换和统一非常重要,使用频率非常高的函数。

select 
   	to_char(current_date,'yyyymmdd') -- 返回当前系统日期,格式为yyyy-mm-dd 【20230408】
    ,to_date('2023-04-26', 'yyyy-mm-dd') -- 将字符串转换成 date 类型的值 【2023-04-26】
    ,date('2023-04-26') -- 将字符串转换成 date 类型的值 【2023-04-26】看字符类型
    ,to_timestamp('2023-04-26', 'yyyy-mm-dd') -- 将字符串转换成 timestamp 类型的值 【2023-04-26 00:00:00】
    ,to_timestamp('2023-04-26 12:30:45','yyyy-mm-dd hh24:mi:ss') -- 将字符串转换成 timestamp 类型的值 【2023-04-26 12:30:45】
    ,to_timestamp('20230426123045','yyyymmddhh24miss')  -- 将字符串转换成 timestamp 类型的值 【2023-04-26 12:30:45】
    ,to_timestamp('2023042612'||'3045','yyyymmddhh24miss')  -- 将字符串转换成 timestamp 类型的值    【2023-04-26 12:30:45】
	,'2023-04-26 16:30:45'::date  		-- 将字符串转换成 date 类型的值  【2023-04-26】
	,'2023-04-26 16:30:45'::time		-- 将字符串转换成  time 类型的值  【16:30:45】
	,'2023-04-26 16:30:45'::timestamp 	-- 将字符串转换成  timestamp 类型的值  【2023-04-26 16:30:45】
;

在这里插入图片描述

转换函数的嵌套使用,这种嵌套在实际工作中非常常用且很实用。

select 
   	to_char(to_date('2023-04-26','yyyy-mm-dd'), 'yyyy年mm月dd日') -- 将日期/时间类型的值转换成字符串类型  【2023年04月26日】
   	,to_char(to_timestamp('2023-04-26 16:30:45','yyyy-mm-dd hh24:mi:ss'),'yyyy/mm/dd hh24时mi分ss秒 ') -- 将字符串转换成 TIMESTAMP 类型的值  【2023/04/26 16时30分45秒】
	,to_char(to_timestamp('2023-04-26 16:30:45','yyyy-mm-dd hh24:mi:ss'),'hh12miss am') -- 将字符串转换成 TIMESTAMP 类型的值   【043045 pm】
;

SQL使用技巧(4.2)Greenplum和PostgreSQL日期时间函数_第1张图片

1.3日期加减计算函数

GreenPlum和PostgreSQL中常用的日期间隔计算函数为age(),或者两个日期直接相减(第2行代码),age()的计算结果是 interval 格式,两个日期直接相减是间隔天数。

select 
	-- 减法
   	interval '2 years 3 months 10 days'   -- 创建一个时间间隔 【2 years 3 mons 10 days】
   	,'2023-04-26'::date - '2021-04-26'::date  -- 两个日期直接相减计算天数 【730】
   	,age('2023-04-26', '2021-12-25')  -- 计算两个日期之间的时间差 【1 year 4 mons 1 day】
   	,age('2023-04-26'::date,'2021-12-25'::date)  -- 计算两个日期之间的时间差 【1 year 4 mons 1 day】
   	,age(timestamp '2023-04-26', timestamp '1950-08-01') -- 计算两个日期之间的时间差 【72 years 8 mons 25 days】
   	,age(timestamp '1950-08-01')   -- 从当前系统日期current_date(2023-04-08) 减去参数后的结果 【72 years 8 mons 7 days】
   	-- 加法
   	,'2023-01-19 09:12:00'::timestamp + interval '1 day'  -- 【2023-01-20 09:12:00】
   	,'2023-01-20 09:12:00'::timestamp + interval '4 week' -- 【2023-02-17 09:12:00】
   	,'2023-01-20 09:12:00'::date + interval '3 month' -- 【2023-04-20 00:00:00】
   	,'2023-01-20 09:12:00'::date + interval '1 year'  -- 【2024-01-20 00:00:00】
   	,'2023-01-20 09:12:00'::date + interval '100 year'  -- 【2123-01-20 00:00:00】
;

减法结果
在这里插入图片描述
加法结果
在这里插入图片描述

1.4日期时间部分提取

函数1:date_part() 从日期/时间字符串中取出指定部分的值
本人比较习惯于使用date_part()函数做日期时间指定值的提取,简单齐全。

select 
   	date_part('year', date '2023-04-26') -- 【2023】
   	,date_part('month', '2023-04-26'::date) -- 【4】
   	,date_part('week', '2023-04-26'::date) -- 【17】
   	,date_part('day', '2023-04-26 19:20:21'::timestamp) -- 【26】
   	,date_part('month', interval '2 years 3 months 10 days') -- 【3】
   	,date_part('year', interval '2 years 3 months 10 days')::text
		||(case when length(date_part('month', interval '2 years 3 months 10 days')::text)=2
				then date_part('month', interval '2 years 3 months 10 days')::text
			else '0'||date_part('month', interval '2 years 3 months 10 days')::text 
		   end)
		||(case when length(date_part('day', interval '2 years 3 months 10 days')::text)=2
				then date_part('day', interval '2 years 3 months 10 days')::text
			else '0'||date_part('day', interval '2 years 3 months 10 days')::text 
		   end)  -- 【20310】 跟DB2的计算一样,这里用五位数字代表间隔的年月日
;

在这里插入图片描述

函数2:extract() 从日期/时间字符串中抽取指定部分的值,与date_part()效果一样

select
	extract(year from timestamp '2023-04-26 19:20:21') -- 【2023】
	,extract(quarter from '2023-04-26 19:20:21'::timestamp) -- 【2】
	,extract(month from '2023-04-26 19:20:21'::timestamp) -- 【4】
	,extract(week from '2023-04-26 19:20:21'::timestamp) -- 【17】
	,extract(day from '2023-04-26 19:20:21'::timestamp) --【26】
	,extract(hour from '2023-04-26 19:20:21'::timestamp) -- 【19】
	,extract(minute from '2023-04-26 19:20:21'::timestamp) -- 【20】
	,extract(second from '2023-04-26 19:20:21'::timestamp) -- 【21】
	-- interval 时间间隔
	,extract(month from interval '2 years 3 months 10 days') -- 【3】
	,extract(year from interval '2 years 3 months 10 days')::text
		||(case when length(extract(month from interval '2 years 3 months 10 days')::text)=2
				then extract(month from interval '2 years 3 months 10 days')::text
			else '0'||extract(month from interval '2 years 3 months 10 days')::text 
		   end)
		||(case when length(extract(day from interval '2 years 3 months 10 days')::text)=2
				then extract(day from interval '2 years 3 months 10 days')::text
			else '0'||extract(day from interval '2 years 3 months 10 days')::text 
		   end) -- 【20310】 跟DB2的计算一样,这里用五位数字代表间隔的年月日
;

在这里插入图片描述

函数3:date_trunc() 截取日期到指定的时间单位,date_trunc()函数的返回可以看到还是个日期时间格式,这是与date_part()和extract()差异所在,计算后直接返回日期时间

select 
   	date_trunc('day',date '2023-04-26')  -- 【2023-04-26 00:00:00】
   	,date_trunc('week', '2023-04-26 19:20:21'::timestamp)  -- 【2023-04-24 00:00:00】
   	,date_trunc('month', '2023-04-26'::date)  -- 【2023-04-01 00:00:00】
   	,date_trunc('year', '2023-04-26'::date)  -- 【2023-01-01 00:00:00】
   	,date_trunc('hour', '2023-04-26 19:20:21'::timestamp)  -- 【2023-04-26 19:00:00】
   	,date_trunc('minute', '2023-04-26 19:20:21'::timestamp)  -- 【2023-04-26 19:20:00】
   	,date_trunc('second', '2023-04-26 19:20:21'::timestamp)  -- 【2023-04-26 19:20:21】
;	

在这里插入图片描述

1.5日期时间函数的嵌套使用

两个日期直接相减,得到的结果是日期间的间隔天数,如下1和2行代码执行结果;
两个日期通过age函数相减,得到结果是 interval 间隔格式,如下3和4行代码执行结果;
3和4行代码执行结果,可以经过 || 拼接加工为类似于DB2的日期相减格式来使用,如五位数字的10407,即1年4个月7天。

select 
   	'2023-04-26'::date - (date_trunc('week','2021-12-26'::date) - interval '1 day') -- 【493 days】
   	,date_part('day','2023-04-26'::date - (date_trunc('week','2021-12-26'::date) - interval '1 day'))  --【493.0】
   	,age('2023-04-26', '2021-12-26')  --【1 year 4 mons】
   	,age('2023-04-26', (date_trunc('week','2021-12-26'::date) - interval '1 day'))  --【1 year 4 mons 7 days】
   	,age('2023-04-26', '2021-12-20'::date - interval '1 day')  -- 【1 year 4 mons 7 days】
   	,date_part('year', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
		||(case when length(date_part('month', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text)=2
				then date_part('month', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
			else '0'||date_part('month', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text 
		   end)
		||(case when length(date_part('day', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text)=2
				then date_part('day', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
			else '0'||date_part('day', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text 
		   end)  -- 【10407】
;

在这里插入图片描述

二.使用案例

2.1存储过程计算日期时间表

第一步,构建表结构。因为某系统经常用到昨日、上周末、上旬末、上月末、上季末、上年同期的日期,每次调用都计算就很麻烦,所以开发了一张日期时间表,每次调用表即可。具体内容如下:

-- 存储表建表
CREATE TABLE public.date_information (
	dtstatdate date NOT NULL, -- 数据日期
	period_code bpchar(3) NOT NULL, -- 内容编码
	period_date date NOT NULL, -- 内容日期
	period_name varchar(10) NOT NULL, -- 内容名称
	period_type int4 NOT NULL, -- 内容格式
	interval_days int4 NOT NULL -- 内容日期与数据日期间隔天数
);
COMMENT ON TABLE public.date_information IS '日期时间表';

-- Column comments

COMMENT ON COLUMN public.date_information.dtstatdate IS '数据日期';
COMMENT ON COLUMN public.date_information.period_code IS '内容编码';
COMMENT ON COLUMN public.date_information.period_date IS '内容日期';
COMMENT ON COLUMN public.date_information.period_name IS '内容名称';
COMMENT ON COLUMN public.date_information.period_type IS '内容格式';
COMMENT ON COLUMN public.date_information.interval_days IS '内容日期与数据日期间隔天数';

第二步,编写存储过程,存储过程较长,完整脚本放在了文章最后;
第三步,调用存储过程生成对应日期,并插入到目标表中。

-- 存储过程调用
select date_inf((current_date - interval '1 day')::date);
select date_inf(('2023-01-21'::date - interval '1 day')::date);

第四步,结果验证

select * from public.date_information;

SQL使用技巧(4.2)Greenplum和PostgreSQL日期时间函数_第2张图片

日期时间间隔计算的完整存储过程如下所示

CREATE OR REPLACE FUNCTION date_inf(dt date)
 RETURNS integer
 LANGUAGE plpgsql
AS $function$
	DECLARE V_STEP VARCHAR(10) default '0';

	begin
		
		V_STEP :='1'; -- 删除历史数据
		delete from date_information where dtstatdate = dt;
		
		V_STEP :='2'; -- 插入select数据
		insert into date_information(dtstatdate,
		period_code,
		period_date,
		period_name,
		period_type,
		interval_days
			)
		select dtstatdate,
			period_code,
			period_date::date period_date,
			period_name,
			period_type,
			interval_days
		from
			(select dt as dtstatdate,
				'CUR' period_code,
				to_char(dt,'yyyy-mm-dd') as period_date,
				'本期' period_name,
				1 period_type,
				0 interval_days
			
			union all
			
			select dt as dtstatdate,
				'PDE' period_code,
				to_char(dt- interval '1 day','yyyy-mm-dd') as period_date,
				'昨日' period_name,
				1 period_type,
				1 interval_days
	
			union all
			
			select dt as dtstatdate,
				'PWE' period_code,
				to_char(date_trunc('week',dt) - interval '1 day','yyyy-mm-dd') as period_date,
				'上周末' period_name,
				1 period_type,
				date_part('day',dt - (date_trunc('week',dt) - interval '1 day')) interval_days
	
			union all
			
			select dt as dtstatdate,
				'PTE' period_code,
				case when substr(dt::text,9,10)::int < 11
						then to_char(concat(substr(dt::text,1,8),'01')::date - interval '1 day','yyyy-mm-dd')
					when substr(dt::text,9,10)::int < 21 
						then to_char(concat(substr(dt::text,1,8),'10')::date,'yyyy-mm-dd')
					when substr(dt::text,9,10)::int >= 21 
						then to_char(concat(substr(dt::text,1,8),'20')::date,'yyyy-mm-dd')
				end as period_date,
				'上旬末' period_name,
				1 period_type,
				date_part('day',dt - (case when substr(dt::text,9,10)::int < 11
						then concat(substr(dt::text,1,8),'01')::date - interval '1 day'
					when substr(dt::text,9,10)::int < 21 
						then concat(substr(dt::text,1,8),'10')::date
					when substr(dt::text,9,10)::int >= 21 
						then concat(substr(dt::text,1,8),'20')::date
				end)) interval_days
	
			union all
			
			select dt as dtstatdate,
				'PME' period_code,
				to_char(date_trunc('month',dt) - interval '1 day','yyyy-mm-dd') as period_date,
				'上月末' period_name,
				1 period_type,
				date_part('day',dt - (date_trunc('month',dt) - interval '1 day')) interval_days
	
			union all
			
			select dt as dtstatdate,
				'PQE' period_code,
				to_char(date_trunc('quarter',dt) - interval '1 day','yyyy-mm-dd') as period_date,
				'上季末' period_name,
				1 period_type,
				date_part('day',dt-(date_trunc('quarter',dt) - interval '1 day')) interval_days
						
			union all
	
			select dt as dtstatdate,
				'PYE' period_code,
				to_char(date_trunc('year',dt) - interval '1 day','yyyy-mm-dd') as period_date,
				'上年末' period_name,
				1 period_type,
				date_part('day',dt-(date_trunc('year',dt) - interval '1 day')) interval_days
	
			union all
	
			select dt as dtstatdate,
				'PYS' period_code,
				to_char(dt - interval '1 year','yyyy-mm-dd') as period_date,
				'上年同期' period_name,
				1 period_type,
				date_part('day',dt-(dt - interval '1 year')) interval_days
				) a
			;
		return 0;
	END;
$function$
;

2.2未完待续


声明:本文所载信息不保证准确性和完整性。文中所述内容和意见仅供参考,不构成实际商业建议,如有雷同纯属巧合。

你可能感兴趣的:(SQL,大数据,sql,数据库)