“每月的第三个星期五”到 PLPGSQL 中的时间戳?
'3rd Friday of the Month' to a timestamp in PLPGSQL?
我有一个数据库列,提供有关文件进入频率的信息。
Frequency_month
-------------
3rd Friday of the month
2nd Tuesday of the month
3rd Thursday of the month
我需要更新此列并将其设置为时间戳。例如
Frequency_month
-------------
2020-05-21 00:00:00
2020-05-11 00:00:00
2020-05-20 00:00:00
如何使用 postgres PLPGSQL 语言完成此操作?
以下是您要查找的内容。就解析 Frequency_month 而言,它施加了以下限制:
- 字符串中的第一个字符是表示亲戚的数字
数.
- 后面是 2 个字符的序号规范(st、nd 等)和一个 space。
其实任意3个字符,都不检查
- 位置 5 - 7 表示英文星期几 (dow) 的前 3 个字符。
如果其中任何一个不满足您将需要更改 S1 子查询。
此外,它还要求您提供参考日期。这可以是感兴趣的月份中的任何日期。请参阅@sddk 的评论。
进行如下:
- 解析以上内容,提取周数、星期几和最后
前一个月的第几天。 (S1).
- 确定指定星期几的 ISODOW id 编号并
上月最后一个月的 DOW。 (S2).
- 使用ISODOW id numbers确定,确定第一个
目标月份中目标日期的发生。 (S3).
- 将#3 中的日期再调整几周。 (S4).
- 最后,如果#4 中的结果日期仍在目标月份
return 日期表 #4。如果不是同一个月,则 return
无效的。这发生在当月没有第 n 个道指或道指
指定不正确。
我已将以上内容包装到一个 SQL 函数中,使参数化变得容易。参见 Demo。
create or replace
function frequency_month( frequency_string text
, target_month date
)
returns date
language sql
as $$
with day_names( l_days) as
( values (array['mon','tue','wed','thu','fri','sat','sun']) )
select -- if the calculated date in still in the target month return that date else return null
-- covers invalid week in frequency 6th Friday or 0th Monday
case when extract(month from target_date) = extract (month from target_month)
then target_date
else null
end
from ( -- Advance from first dow in month the number of weeks to desirded dates
--select (first_of_mon + (7*(rel_num-1)) * interval '1 day')::date target_date
select (first_of_mon + (rel_num-1) * interval '1 week')::date target_date
from ( -- with last day of prior month get first DOW week of target month
select case when dow_day_nbr <= from_day_nbr
then (from_date + (dow_day_nbr-from_day_nbr+7) * interval '1 days' )::date
else (from_date + (dow_day_nbr-from_day_nbr) * interval '1 days' )::date
end first_of_mon
, rel_num
from ( -- Pick up ISODOW numbers
select array_position(l_days, (substring(to_char(from_date, 'day'),1,3))) as from_day_nbr
, array_position(l_days, lower(substring(rel_dow,1,3))) as dow_day_nbr
, from_date
, rel_num
from day_names
cross join ( -- get last day of prior month, desired relative day, relative dow
select substr(frequency_string,1,1)::integer rel_num
, lower(substr(frequency_string,5,3)) rel_dow
, (date_trunc('month',target_month) - interval '1 day')::date from_date
) s1
) s2
) s3
) s4;
$$;
注意:如果不需要某个功能,该演示还包括独立版本。
我有一个数据库列,提供有关文件进入频率的信息。
Frequency_month
-------------
3rd Friday of the month
2nd Tuesday of the month
3rd Thursday of the month
我需要更新此列并将其设置为时间戳。例如
Frequency_month
-------------
2020-05-21 00:00:00
2020-05-11 00:00:00
2020-05-20 00:00:00
如何使用 postgres PLPGSQL 语言完成此操作?
以下是您要查找的内容。就解析 Frequency_month 而言,它施加了以下限制:
- 字符串中的第一个字符是表示亲戚的数字 数.
- 后面是 2 个字符的序号规范(st、nd 等)和一个 space。 其实任意3个字符,都不检查
- 位置 5 - 7 表示英文星期几 (dow) 的前 3 个字符。
如果其中任何一个不满足您将需要更改 S1 子查询。 此外,它还要求您提供参考日期。这可以是感兴趣的月份中的任何日期。请参阅@sddk 的评论。
进行如下:
- 解析以上内容,提取周数、星期几和最后 前一个月的第几天。 (S1).
- 确定指定星期几的 ISODOW id 编号并 上月最后一个月的 DOW。 (S2).
- 使用ISODOW id numbers确定,确定第一个 目标月份中目标日期的发生。 (S3).
- 将#3 中的日期再调整几周。 (S4).
- 最后,如果#4 中的结果日期仍在目标月份 return 日期表 #4。如果不是同一个月,则 return 无效的。这发生在当月没有第 n 个道指或道指 指定不正确。
我已将以上内容包装到一个 SQL 函数中,使参数化变得容易。参见 Demo。
create or replace
function frequency_month( frequency_string text
, target_month date
)
returns date
language sql
as $$
with day_names( l_days) as
( values (array['mon','tue','wed','thu','fri','sat','sun']) )
select -- if the calculated date in still in the target month return that date else return null
-- covers invalid week in frequency 6th Friday or 0th Monday
case when extract(month from target_date) = extract (month from target_month)
then target_date
else null
end
from ( -- Advance from first dow in month the number of weeks to desirded dates
--select (first_of_mon + (7*(rel_num-1)) * interval '1 day')::date target_date
select (first_of_mon + (rel_num-1) * interval '1 week')::date target_date
from ( -- with last day of prior month get first DOW week of target month
select case when dow_day_nbr <= from_day_nbr
then (from_date + (dow_day_nbr-from_day_nbr+7) * interval '1 days' )::date
else (from_date + (dow_day_nbr-from_day_nbr) * interval '1 days' )::date
end first_of_mon
, rel_num
from ( -- Pick up ISODOW numbers
select array_position(l_days, (substring(to_char(from_date, 'day'),1,3))) as from_day_nbr
, array_position(l_days, lower(substring(rel_dow,1,3))) as dow_day_nbr
, from_date
, rel_num
from day_names
cross join ( -- get last day of prior month, desired relative day, relative dow
select substr(frequency_string,1,1)::integer rel_num
, lower(substr(frequency_string,5,3)) rel_dow
, (date_trunc('month',target_month) - interval '1 day')::date from_date
) s1
) s2
) s3
) s4;
$$;
注意:如果不需要某个功能,该演示还包括独立版本。