Clickhouse SQL日期处理函数及案例分享
适用场景
高性能数据集,以及Clickhouse直连数据集。
注意事项
-
所有涉及到时分秒(DateTime字段)的函数都可能需要时区参数 timezone,时区格式一般为UTC时区或地理位置的IANA标识符(例如 Europe/Moscow),未指定时区则默认转换为服务器的时区(例如世界标准时间UTC)。
-
部分函数大小写敏感:大小写混写的函数为Clickhouse专有函数,必须原样使用(例如 toYear);全部为大写或小写的函数为兼容其他数据库的函数,大小写不敏感,即大小写都行(例如 YEAR)。
-
以下函数非全部可用函数,是基于BI当前所用v22版本汇编而成的常用函数。更多函数请参考Clickhouse官方网站Dates and Times。
日期支持函数
时间或日期截取函数
A) 原字段为日期(date)或日期时间(datetime/timestamp), 返回非日期。
用途 | 函数 | 举例 | 结果 |
取年份 | toYear() / toISOYear() /YEAR() | toYear('2018-12-11 11:12:13') | 2018 |
取季度数 | toQuarter() / QUARTER() | toQuarter('2018-12-11 11:12:13') | 4 |
取月份 | toMonth() / MONTH() | toMonth('2018-12-11 11:12:13') | 12 |
monthName(date) | monthName('2018-12-11 11:12:13') | December | |
取当月内的天数(1-31) | toDayOfMonth() / DAYOFMONTH() / DAY() | toDayOfMonth('2019-12-03') | 3 |
取当年内的天数(1-365) | toDayOfYear() / DAYOFYEAR() | toDayOfYear('2019-12-03') | 337 |
取星期 | toDayOfWeek()/DAYOFWEEK() 周一是1, 周日是7 | toDayOfWeek('2019-12-03') | 2 |
取小时 | toHour() / HOUR() | toHour('2018-12-11 11:12:13') | 11 |
取分钟 | toMinute() / MINUTE() | toMinute('2018-12-11 11:12:13') | 12 |
取秒 | toSecond() / SECOND() | toSecond('2018-12-11 11:12:13') | 13 |
取ISO周数 (周一起始,第一周需包含当年天数>3) | toISOWeek() / toWeek(date,3) | toISOWeek('2019-12-03') | 49 |
取时分秒 | formatDateTime(Time, Format) | formatDateTime('2018-12-11 11:12:13','%T') | 11:12:13 |
取日期时间里的特定部分 | dateName(date_part,date) | dateName('weekday','2018-12-11 11:12:13') | Tuesday |
B) 原字段为日期(date)或日期时间(datetime/timestamp), 返回日期或时间。
用途 | 函数 | 举例 | 结果 |
取所在周的周一 | toMonday() | toMonday('2019-12-03 09:00:00') | 2019-12-02 |
取所在周的第一天 [,mode]缺省默认为0 | toStartOfWeek(t[,mode]) | toStartOfWeek('2019-12-03 09:00:00',3) | 2019-12-02 |
取所在月第一天 | toStartOfMonth() | toStartOfMonth('2019-12-03 09:00:00') | 2019-12-01 |
取所在季第一天 | toStartOfQuarter() | toStartOfQuarter('2019-12-03 09:00:00') | 2019-10-01 |
取所在年第一天 | toStartOfYear() | toStartOfYear('2019-12-03 09:00:00') | 2019-01-01 |
toStartOfISOYear() | toStartOfISOYear('2019-12-03 09:00:00') | 2018-12-31 | |
截取时间日期到天(之后归零) | toStartOfDay() | toStartOfDay('2019-12-03 09:00:00') | 2019-12-03 00:00:00 |
截取时间日期到小时(之后归零) | toStartOfHour(value[, timezone]) | toStartOfHour('2021-11-30 13:51:35','Asia/Shanghai') | 2021-11-30 13:00:00 |
截取时间日期到分钟(之后归零) | toStartOfMinute(value[, timezone]) | toStartOfHour('2021-11-30 13:51:35','Asia/Shanghai') | 2021-11-30 13:51:00 |
将DateTime以五分钟为单位向前取整到最接近的时间点 | toStartOfFiveMinute(value[, timezone]) | toStartOfFiveMinute('2021-11-30 13:51:35','Asia/Shanghai') | 2021-11-30 13:50:00 |
将DateTime以十分钟为单位向前取整到最接近的时间点 | toStartOfTenMinutes(value[, timezone]) | toStartOfTenMinutes('2021-11-30 13:51:35','Asia/Shanghai') | 2021-11-30 13:50:00 |
将DateTime以十五分钟为单位向前取整到最接近的时间点 | toStartOfFifteenMinutes(value[, timezone]) | toStartOfFifteenMinutes('2021-11-30 13:51:35','Asia/Shanghai') | 2021-11-30 13:45:00 |
将DateTime以自定义单位向前取整到最接近的时间点 | toStartOfInterval(time_or_data,间隔x单位[,time_zone]) | toStartOfInterval('2021-11-30 13:51:35',INTERVAL 20 minute,'Asia/Shanghai') | 2021-11-30 13:40:00 |
将时间向前取整半小时 | timeSlot() | timeSlot('2021-12-02 16:39:09','Asia/Shanghai') | 2021-12-02 16:30:00 |
截取时间日期到特定部分(之后归零), 返回Date/Datetime | date_trunc(unit,value[, timezone] / dateTrunc(unit,value[, timezone] | date_trunc('hour', '2021-12-02 16:39:09')dateTrunc('week', '2021-12-02 16:39:09') | 2021-12-02 16:00:00 |
日期或时间日期生成函数
用途 | 函数 | 结果 |
生成当前时间日期,可指定时区 | now() | 2021-12-01 20:00:00 |
生成今天的日期 | today() | 2021-12-01 |
生成昨天的日期 | yesterday() / today() - 1 | 2021-11-30 |
生成当前时间戳 | toUnixTimestamp(now()) | 1638388800 |
日期与时间计算
用途 | 函数 | 举例 | 结果 |
增减日期时间 | +/- interval n unit' (中间数值n不能引用其他字段) | '2021-07-30 15:48:08' - interval 1 year | 2020-07-30 15:48:08 |
'2021-07-30 15:48:08' + interval 2 hour | 2021-07-30 17:48:08 | ||
直接加减数字, date按照天数加减; datetime按照秒数加减 | '2021-07-30 15:48:08' - 10 | 2021-07-30 15:47:52 | |
'2021-07-30' - 10 | 2021-07-20 | ||
toInterval(Year|Quarter|Month|Week|Day|Hour|Minute|Second) (number) number — 正整数,持续的时间, 可引用其他int字段 | '2021-07-30'+toIntervalDay(7) | 2021-08-06 | |
'2021-07-30 15:48:08' - toIntervalHour(7) | 2021-07-30 08:48:08 | ||
计算未来日期 | addYears, addMonths, addWeeks, addDays, addHours, addMinutes, addSeconds, addQuarters | addHours('2019-12-03 09:00:00', 1, 'Asia/Shanghai') | 2019-12-03 10:00:00 |
addWeeks('2019-12-03',1) | 2019-12-10 | ||
计算过去日期 | subtractYears, subtractMonths, subtractWeeks, subtractDays, subtractours, subtractMinutes, subtractSeconds, subtractQuarters | subtractQuarters('2021-12-03',1) | 2021-09-03 |
subtractDays('2019-12-03 09:00:00',3,'Asia/Shanghai') | 2019-11-30 09:00:00 | ||
计算日期时间差 --返回整数int | dateDiff('unit', startdate, enddate, [timezone]) | dateDiff('month', '2020-12-02', '2021-11-30') | 11 |
dateDiff('hour','2021-11-30 08:00:00', '2021-11-30 17:36:08','Asia/Shanghai') | 9 |
日期与时间日期转化
用途 | 函数 | 举例 | 结果 |
将字符型日期转化为日期型 | toDate() | toDate('2009-07-30 04:17:52') | 2009-07-30 |
cast(str,'date')/cast(str as date) | cast('2009-07-30 04:17:52','date') | ||
将字符型日期或者时间戳转化为时间日期型 | toDateTime() | toDateTime('2022-01-01 13:00:00','Asia/Shanghai') | 2022-01-01 13:00:00 |
cast(str,'datetime')/cast(str as datetime) | cast(today() as datetime) | 2021-11-30 08:00:00 | |
将数值型、日期型等格式转化为字符型 | toString() | toString('2021-07-30 15:48:08') | 2021-07-30 15:48:08 |
cast(time,'String') /cast(time as String) | cast('2021-07-30 15:48:08' as String) | ||
日期时间转换为时间戳 | toUnixTimestamp(time[, timezone]) | toUnixTimestamp('2019-12-03 09:00:00') | 1575334800 |
日期时间格式转化,结果一般为字符型或数值型 | formatDateTime(Time, Format \ [,Timezone\]) | formatDateTime('2021-12-02 15:48:52', '%Y/%m/%d %I:%M','Asia/Shanghai') | 2021/12/02 03:48 |
日期时间格式转化,结果为数值型 | toYYYYMM() | toYYYYMM('2021-12-02') | 202112 |
toYYYYMMDD() | toYYYYMMDD('2021-12-02') | 20211202 | |
toYYYYMMDDhhmmss() | toYYYYMMDDhhmmss('2021-12-02 16:00:09','Asia/Shanghai') | 20211202160009 | |
toYearWeek(date[,mode]) | toYearWeek('2019-12-03') | 201949 | |
时区偏移转换 | toTimeZone(Time, Timezone) | toTimeZone('2021-12-02 16:00:09','US/Samoa') | 2021-12-01 21:00:09 |
把String类型的时间日期转换为DateTime类型 | parseDateTimeBestEffort() | 案例参考下图 |
案例
案例一:文本类型日期转换为标准日期格式
文本日期 | 标准格式 | 函数 | |
20210808121600 | 2021-08-08 12:16:00 | parseDateTimeBestEffort([文本日期],'Asia/Shanghai') | |
2021/07/30 13:30:00 | 2021-07-30 13:30:00 | ||
30/7/2021 01:30 PM | 2021-07-30 13:30:00 | ||
2021-07-30T16:00:00.000Z | 2021-07-31 00:00:00 | parseDateTimeBestEffort([文本日期]) | |
2021-07-30T17:25:53+00:00 | 2021-07-31 17:25:53 | parseDateTimeBestEffort([文本日期],'UTC') | |
Sat, 18 Aug 2018 07:22:16 GMT | 2018-08-18 07:22:16 | ||
July 30, 2021 | 2021-07-30 | toDate(parseDateTimeBestEffort(replaceOne([文本日期],',',''))) | |
Aug 8, 2021 | 2021-08-08 | ||
30/07/2021 | 2021-07-30 | toDate(parseDateTimeBestEffort([文本日期])) | |
07/30/2021 | 2021-07-30 | toDate(replaceRegexpOne([文本日期],'(\\d{2})/(\\d{2})/(\\d{4})','\\3-\\1-\\2')) | |
2021年7月30日 | 2021-07-30 | toDate(replaceRegexpOne([文本日期],'(\\d{4})年(\\d{1,2})月(\\d{1,2})日','\\1-\\2-\\3')) |
案例二:标准日期转换为文本类型案例
日期 | 目标格式(文本) | 函数 | |
2021-08-08 15:16:00 | 2021-08 | formatDateTime([日期],'%Y-%m') | |
202108 (数值) | toYYYYMM([日期]) | ||
08/08 | formatDateTime([日期],'%m-%d') | ||
15:16:00 | formatDateTime([日期],'%R','Asia/Shanghai') | ||
03:16 PM | formatDateTime([日期],'%I:%M %p','Asia/Shanghai') | ||
2021年8月8日 | formatDateTime([日期],'%Y年%m月%d日') | ||
2021-08-08 15:16:00 (+08:00) | concat([日期],' (+08:00)') | ||
Aug 8, 2021 | 不低于v21.7版本:concat(left(dateName('month',[日期]) ),3),' ', toString(Day([日期])),',',toString(toYear([日期]))) | ||
Sunday | dateName('weekday',[日期]) |
附录:
-
formatDateTime 函数支持的格式修饰符
“举例”列是对2018-01-02 22:33:44的格式化结果:
符号 | 含义 | 举例 |
%C | 年除以100并截断为整数(00-99) | 20 |
%d | 月中的一天,零填充(01-31) | 2 |
%D | 短MM/DD/YY日期,相当于%m/%d/%y | 01/02/2018 |
%e | 月中的一天,空格填充( 1-31) | 2 |
%F | 短YYYY-MM-DD日期,相当于%Y-%m-%d | 2018/1/2 |
%G | ISO周号的四位数年份格式, 从基于周的年份由ISO 8601定义 标准计算得出,通常仅对%V有用 | 2018 |
%g | 两位数的年份格式,与ISO 8601一致,四位数表示法的缩写 | 18 |
%H | 24小时格式(00-23) | 22 |
%I | 12小时格式(01-12) | 10 |
%j | 一年中的一天 (001-366) | 2 |
%m | 月份为十进制数(01-12) | 1 |
%M | 分钟(00-59) | 33 |
%n | 换行符(") | |
%p | AM或PM指定 | PM |
%Q | 季度(1-4) | 1 |
%R | 24小时HH:MM时间,相当于%H:%M | 22:33 |
%S | 秒 (00-59) | 44 |
%t | 水平制表符(’) | |
%T | ISO8601时间格式(HH:MM:SS),相当于%H:%M:%S | 22:33:44 |
%u | ISO8601工作日为数字,星期一为1(1-7) | 2 |
%V | ISO8601周编号(01-53) | 1 |
%w | 工作日为十进制数,周日为0(0-6) | 2 |
%y | 年份,最后两位数字(00-99) | 18 |
%Y | 年 | 2018 |
%% | %符号 | % |
-
周数计算需要使用mode参数。
该参数可以指定星期是从星期日还是星期一开始,以及返回值应在0到53还是从1到53的范围内。取值范围 [0,9],如果省略了mode参数,则默认模式为0。使用mode参数的函数有 toWeek(date[,mode]),toYearWeek(date[,mode]),toStartOfWeek(t[,mode])。toISOWeek()是一个兼容函数,等效于toWeek(date,3)。
下表描述了mode参数的工作方式:
Mode | First day of week | Range | Week 1 is the first week … |
0 | Sunday | 0-53 | with a Sunday in this year |
1 | Monday | 0-53 | with 4 or more days this year |
2 | Sunday | 1-53 | with a Sunday in this year |
3 | Monday | 1-53 | with 4 or more days this year |
4 | Sunday | 0-53 | with 4 or more days this year |
5 | Monday | 0-53 | with a Monday in this year |
6 | Sunday | 1-53 | with 4 or more days this year |
7 | Monday | 1-53 | with a Monday in this year |
8 | Sunday | 1-53 | contains January 1 |
9 | Monday | 1-53 | contains January 1 |
对于象“with 4 or more days this year,”的mode值,根据ISO 8601:1988对周进行编号:
-
如果包含1月1日的一周在后一年度中有4天或更多天,则为第1周。
-
否则,它是上一年的最后一周,下周是第1周。
对于像“contains January 1”的mode值, 包含1月1日的那周为本年度的第1周。