SQL: 存储和查询 `cron` 字段
SQL: Store and query `cron` field
我正在构建一个 cron-as-a-service,允许用户输入他们的 cron 表达式,并定期做一些事情。
这是我的 table:
的简单版本
create table users (
id serial not null primary key,
-- `text` seems the most straightforward data type, any other suggestions?
cron text not null,
constraint valid_cron_expression CHECK (cron ~* '{some_regex}'),
-- snip --
-- maybe some other fields, like what to do on each cron call
)
我的后端每分钟运行一次 SQL 查询。如何查询 cron
字段匹配当前时间戳(四舍五入到最接近的分钟)的所有行?
编辑:用户将 cron
字段输入为 cron expression,例如5 4 * * *
.
编辑 2:更正了 cron 时间分辨率是分钟而不是秒的事实。
这解决了问题的原始版本。
假设 cron
是某种时间戳,您将使用:
where cron >= date_trunc('second', now()) and
cron < date_trun('second', now()) + interval '1 second'
首先,您不需要每秒查询一次,因为 cron 的分辨率只有一分钟。
接下来,将 cron 调度程序表达式与时间戳进行比较并非易事。
我不知道任何能够解析 cron 表达式的 PostgreSQL 模块。
有两种选择,要么编写自己的函数来进行比较,要么使用所用编程语言中的外部库在数据库外部进行比较。
在这里您将找到一个可以轻松移植到 PostgreSQL 的 Oracle 函数的示例实现:SQL Query to convert cron expression to date/time format
它不完整,因为它不处理 cron 表达式的各个字段的复杂表达式,如 */5 或 5,10,15,但这是我要开始的地方。
您可能需要调整一些函数来匹配您的 SQL 方言,但这似乎可行,您在这里唯一输入的是 ETL_CRON 的值,输出是布尔值 RUN_ETL.
示例 returns 在星期一、星期二、星期三、星期四和星期五的每小时 3 点到 59 点的每 12 分钟为 TRUE。:
select
'3/12 2-22 * * 1,2,3,4,5' as etl_cron
, split_part(etl_cron,' ',1) as etl_minute
, split_part(etl_cron,' ',2) as etl_hour
, split_part(etl_cron,' ',3) as etl_daymonth
, split_part(etl_cron,' ',4) as etl_month
, split_part(etl_cron,' ',5) as etl_dayweek
, convert_timezone('Europe/Amsterdam',current_timestamp) as etl_datetime
, case
when etl_minute = '*' then true
when contains(etl_minute,'-') then minute(etl_datetime) between split_part(etl_minute,'-',1) and split_part(etl_minute,'-',2)
when contains(etl_minute,'/') then mod(minute(etl_datetime),split_part(etl_minute,'/',2)) - split_part(etl_minute,'/',1) = 0
else array_contains(minute(etl_datetime)::varchar::variant, split(etl_minute,','))
end as run_minute
, case
when etl_hour = '*' then true
when contains(etl_hour,'-') then hour(etl_datetime) between split_part(etl_hour,'-',1) and split_part(etl_hour,'-',2)
when contains(etl_hour,'/') then mod(hour(etl_datetime),split_part(etl_hour,'/',2)) - split_part(etl_hour,'/',1) = 0
else array_contains(hour(etl_datetime)::varchar::variant, split(etl_hour,','))
end as run_hour
, case
when etl_daymonth = '*' then true
when contains(etl_daymonth,'-') then day(etl_datetime) between split_part(etl_daymonth,'-',1) and split_part(etl_daymonth,'-',2)
when contains(etl_daymonth,'/') then mod(day(etl_datetime),split_part(etl_daymonth,'/',2)) - split_part(etl_daymonth,'/',1) = 0
else array_contains(day(etl_datetime)::varchar::variant, split(etl_daymonth,','))
end as run_daymonth
, case
when etl_month = '*' then true
when contains(etl_month,'-') then month(etl_datetime) between split_part(etl_month,'-',1) and split_part(etl_month,'-',2)
when contains(etl_month,'/') then mod(month(etl_datetime),split_part(etl_month,'/',2)) - split_part(etl_month,'/',1) = 0
else array_contains(month(etl_datetime)::varchar::variant, split(etl_month,','))
end as run_month
, case
when etl_dayweek = '*' then true
when contains(etl_dayweek,'-') then dayofweek(etl_datetime) between split_part(etl_dayweek,'-',1) and split_part(etl_dayweek,'-',2)
when contains(etl_dayweek,'/') then mod(dayofweek(etl_datetime),split_part(etl_dayweek,'/',2)) - split_part(etl_dayweek,'/',1) = 0
else array_contains(dayofweek(etl_datetime)::varchar::variant, split(etl_dayweek,','))
end as run_dayweek
, run_minute and run_hour and run_daymonth and run_month and run_dayweek as run_etl;
我正在构建一个 cron-as-a-service,允许用户输入他们的 cron 表达式,并定期做一些事情。
这是我的 table:
的简单版本create table users (
id serial not null primary key,
-- `text` seems the most straightforward data type, any other suggestions?
cron text not null,
constraint valid_cron_expression CHECK (cron ~* '{some_regex}'),
-- snip --
-- maybe some other fields, like what to do on each cron call
)
我的后端每分钟运行一次 SQL 查询。如何查询 cron
字段匹配当前时间戳(四舍五入到最接近的分钟)的所有行?
编辑:用户将 cron
字段输入为 cron expression,例如5 4 * * *
.
编辑 2:更正了 cron 时间分辨率是分钟而不是秒的事实。
这解决了问题的原始版本。
假设 cron
是某种时间戳,您将使用:
where cron >= date_trunc('second', now()) and
cron < date_trun('second', now()) + interval '1 second'
首先,您不需要每秒查询一次,因为 cron 的分辨率只有一分钟。
接下来,将 cron 调度程序表达式与时间戳进行比较并非易事。 我不知道任何能够解析 cron 表达式的 PostgreSQL 模块。
有两种选择,要么编写自己的函数来进行比较,要么使用所用编程语言中的外部库在数据库外部进行比较。
在这里您将找到一个可以轻松移植到 PostgreSQL 的 Oracle 函数的示例实现:SQL Query to convert cron expression to date/time format
它不完整,因为它不处理 cron 表达式的各个字段的复杂表达式,如 */5 或 5,10,15,但这是我要开始的地方。
您可能需要调整一些函数来匹配您的 SQL 方言,但这似乎可行,您在这里唯一输入的是 ETL_CRON 的值,输出是布尔值 RUN_ETL. 示例 returns 在星期一、星期二、星期三、星期四和星期五的每小时 3 点到 59 点的每 12 分钟为 TRUE。:
select
'3/12 2-22 * * 1,2,3,4,5' as etl_cron
, split_part(etl_cron,' ',1) as etl_minute
, split_part(etl_cron,' ',2) as etl_hour
, split_part(etl_cron,' ',3) as etl_daymonth
, split_part(etl_cron,' ',4) as etl_month
, split_part(etl_cron,' ',5) as etl_dayweek
, convert_timezone('Europe/Amsterdam',current_timestamp) as etl_datetime
, case
when etl_minute = '*' then true
when contains(etl_minute,'-') then minute(etl_datetime) between split_part(etl_minute,'-',1) and split_part(etl_minute,'-',2)
when contains(etl_minute,'/') then mod(minute(etl_datetime),split_part(etl_minute,'/',2)) - split_part(etl_minute,'/',1) = 0
else array_contains(minute(etl_datetime)::varchar::variant, split(etl_minute,','))
end as run_minute
, case
when etl_hour = '*' then true
when contains(etl_hour,'-') then hour(etl_datetime) between split_part(etl_hour,'-',1) and split_part(etl_hour,'-',2)
when contains(etl_hour,'/') then mod(hour(etl_datetime),split_part(etl_hour,'/',2)) - split_part(etl_hour,'/',1) = 0
else array_contains(hour(etl_datetime)::varchar::variant, split(etl_hour,','))
end as run_hour
, case
when etl_daymonth = '*' then true
when contains(etl_daymonth,'-') then day(etl_datetime) between split_part(etl_daymonth,'-',1) and split_part(etl_daymonth,'-',2)
when contains(etl_daymonth,'/') then mod(day(etl_datetime),split_part(etl_daymonth,'/',2)) - split_part(etl_daymonth,'/',1) = 0
else array_contains(day(etl_datetime)::varchar::variant, split(etl_daymonth,','))
end as run_daymonth
, case
when etl_month = '*' then true
when contains(etl_month,'-') then month(etl_datetime) between split_part(etl_month,'-',1) and split_part(etl_month,'-',2)
when contains(etl_month,'/') then mod(month(etl_datetime),split_part(etl_month,'/',2)) - split_part(etl_month,'/',1) = 0
else array_contains(month(etl_datetime)::varchar::variant, split(etl_month,','))
end as run_month
, case
when etl_dayweek = '*' then true
when contains(etl_dayweek,'-') then dayofweek(etl_datetime) between split_part(etl_dayweek,'-',1) and split_part(etl_dayweek,'-',2)
when contains(etl_dayweek,'/') then mod(dayofweek(etl_datetime),split_part(etl_dayweek,'/',2)) - split_part(etl_dayweek,'/',1) = 0
else array_contains(dayofweek(etl_datetime)::varchar::variant, split(etl_dayweek,','))
end as run_dayweek
, run_minute and run_hour and run_daymonth and run_month and run_dayweek as run_etl;