您如何在 Postgresql 中获得动态的 12 个工作日视图?
How do you get a dynamic 12 business day view in Postgresql?
这是我目前拥有的代码,它提供了我过去 12 天的数据
SELECT *
FROM table
WHERE analysis_date >= current_date - interval '12' day;
analysis_date 是 table 中的日期列。我明白为什么这不起作用,因为它没有计算工作日。我怎样才能重写这个,以便我得到最近 12 个工作日的时间间隔?
我尝试在线搜索并找到
extract(dow from (date))
但我找不到需要工作日间隔的示例。任何帮助,将不胜感激。
这可以用 CTE 解决:
WITH business_days_back AS (
WITH RECURSIVE bd(back_day, go_back) AS (
-- Go back to the previous Monday, allowing for current_date in the weekend
SELECT CASE extract(dow from current_date)
WHEN 0 THEN current_date - 6
WHEN 6 THEN current_date - 5
ELSE current_date - extract(dow from current_date)::int + 1
END,
CASE extract(dow from current_date)
WHEN 0 THEN 7
WHEN 6 THEN 7
ELSE 12 - extract(dow from current_date)::int + 1
END
UNION
-- Go back by the week until go_back = 0
SELECT CASE
WHEN go_back >= 5 THEN back_day - 7
WHEN go_back > 0 THEN back_day - 2 - go_back
END,
CASE
WHEN go_back >= 5 THEN go_back - 5
WHEN go_back > 0 THEN 0
END
FROM bd
)
SELECT back_day FROM bd WHERE go_back = 0
)
SELECT * FROM my_table WHERE analysis_date >= (SELECT * FROM business_days_back);
一些解释:
- 内部 CTE 从回到上周一开始,补偿
current_date
周末的一天。
- 递归项然后通过返回整周(
back_day - 7
用于日历日期,go_back - 5
用于工作日)添加行,直到 go_back = 0
.
- 外部 CTE returns
back_day
日期 go_back = 0
。因此,这是一个标量查询,您可以将其用作过滤器表达式中的子查询。
您只需更改内部 CTE 中初始 SELECT
中的数字 12
和 7
,即可更改回顾的工作日数。但请记住,由于内部 CTE 的初始 SELECT
相同,该值应该可以追溯到前一个星期一,否则查询将失败。
更灵活(并且可能更快*)的解决方案是使用以下函数:
CREATE FUNCTION business_days_diff(from_date date, diff int) RETURNS date AS $$
-- This function assumes Mon-Fri business days
DECLARE
start_dow int;
calc_date date;
curr_diff int;
weekend int;
BEGIN
-- If no diff requested, return the from_date. This may be a non-business day.
IF diff = 0 THEN
RETURN from_date;
END IF;
start_dow := extract(dow from from_date)::int;
calc_date := from_date;
IF diff < 0 THEN -- working backwards
weekend := -2;
IF start_dow = 0 THEN -- Fudge initial Sunday to the previous Saturday
calc_date := calc_date - 1;
start_dow := 6;
END IF;
IF start_dow + diff >= 1 THEN -- Stay in this week
RETURN calc_date + diff;
ELSE -- Work back to Monday
calc_date := calc_date - start_dow + 1;
curr_diff := diff + start_dow - 1;
END IF;
ELSE -- Working forwards
weekend := 2;
IF start_dow = 6 THEN -- Fudge initial Saturday to the following Sunday
calc_date := calc_date + 1;
start_dow := 0;
END IF;
IF start_dow + diff <= 5 THEN -- Stay in this week
RETURN calc_date + diff;
ELSE -- Work forwards to Friday
calc_date := calc_date + 5 - start_dow;
curr_diff := diff - 5 + start_dow;
END IF;
END IF;
-- Move backwards or forwards by full weeks
calc_date := calc_date + (curr_diff / 5) * 7;
-- Process any remaining days, include weekend
IF curr_diff % 5 != 0 THEN
RETURN calc_date + curr_diff % 5 + weekend;
ELSE
RETURN calc_date;
END IF;
END; $$ LANGUAGE plpgsql STRICT IMMUTABLE;
此函数可以使用任何日期来计算未来(diff
的正值)或过去(diff
的负值)的任意天数,包括当前一周。由于它 returns 工作日日期作为标量,因此在您的查询中使用非常简单:
SELECT *
FROM table
WHERE analysis_date >= business_days_diff(current_date, -12);
除此之外,您还可以传入 table 中的字段并执行一些时髦的操作,例如:
SELECT t1.some_value - t2.some_value AS value_diff
FROM table t1
JOIN table t2 ON t2.analysis_date = business_days_diff(t1.analysis_date, -12);
即一定数量的工作日分离的自连接。
请注意,此函数假定工作日为周一至周五。
* 此函数仅对标量值进行简单的算术运算。 CTE 必须设置各种结构来支持迭代和生成的记录集。
这是我目前拥有的代码,它提供了我过去 12 天的数据
SELECT *
FROM table
WHERE analysis_date >= current_date - interval '12' day;
analysis_date 是 table 中的日期列。我明白为什么这不起作用,因为它没有计算工作日。我怎样才能重写这个,以便我得到最近 12 个工作日的时间间隔?
我尝试在线搜索并找到
extract(dow from (date))
但我找不到需要工作日间隔的示例。任何帮助,将不胜感激。
这可以用 CTE 解决:
WITH business_days_back AS (
WITH RECURSIVE bd(back_day, go_back) AS (
-- Go back to the previous Monday, allowing for current_date in the weekend
SELECT CASE extract(dow from current_date)
WHEN 0 THEN current_date - 6
WHEN 6 THEN current_date - 5
ELSE current_date - extract(dow from current_date)::int + 1
END,
CASE extract(dow from current_date)
WHEN 0 THEN 7
WHEN 6 THEN 7
ELSE 12 - extract(dow from current_date)::int + 1
END
UNION
-- Go back by the week until go_back = 0
SELECT CASE
WHEN go_back >= 5 THEN back_day - 7
WHEN go_back > 0 THEN back_day - 2 - go_back
END,
CASE
WHEN go_back >= 5 THEN go_back - 5
WHEN go_back > 0 THEN 0
END
FROM bd
)
SELECT back_day FROM bd WHERE go_back = 0
)
SELECT * FROM my_table WHERE analysis_date >= (SELECT * FROM business_days_back);
一些解释:
- 内部 CTE 从回到上周一开始,补偿
current_date
周末的一天。 - 递归项然后通过返回整周(
back_day - 7
用于日历日期,go_back - 5
用于工作日)添加行,直到go_back = 0
. - 外部 CTE returns
back_day
日期go_back = 0
。因此,这是一个标量查询,您可以将其用作过滤器表达式中的子查询。
您只需更改内部 CTE 中初始 SELECT
中的数字 12
和 7
,即可更改回顾的工作日数。但请记住,由于内部 CTE 的初始 SELECT
相同,该值应该可以追溯到前一个星期一,否则查询将失败。
更灵活(并且可能更快*)的解决方案是使用以下函数:
CREATE FUNCTION business_days_diff(from_date date, diff int) RETURNS date AS $$
-- This function assumes Mon-Fri business days
DECLARE
start_dow int;
calc_date date;
curr_diff int;
weekend int;
BEGIN
-- If no diff requested, return the from_date. This may be a non-business day.
IF diff = 0 THEN
RETURN from_date;
END IF;
start_dow := extract(dow from from_date)::int;
calc_date := from_date;
IF diff < 0 THEN -- working backwards
weekend := -2;
IF start_dow = 0 THEN -- Fudge initial Sunday to the previous Saturday
calc_date := calc_date - 1;
start_dow := 6;
END IF;
IF start_dow + diff >= 1 THEN -- Stay in this week
RETURN calc_date + diff;
ELSE -- Work back to Monday
calc_date := calc_date - start_dow + 1;
curr_diff := diff + start_dow - 1;
END IF;
ELSE -- Working forwards
weekend := 2;
IF start_dow = 6 THEN -- Fudge initial Saturday to the following Sunday
calc_date := calc_date + 1;
start_dow := 0;
END IF;
IF start_dow + diff <= 5 THEN -- Stay in this week
RETURN calc_date + diff;
ELSE -- Work forwards to Friday
calc_date := calc_date + 5 - start_dow;
curr_diff := diff - 5 + start_dow;
END IF;
END IF;
-- Move backwards or forwards by full weeks
calc_date := calc_date + (curr_diff / 5) * 7;
-- Process any remaining days, include weekend
IF curr_diff % 5 != 0 THEN
RETURN calc_date + curr_diff % 5 + weekend;
ELSE
RETURN calc_date;
END IF;
END; $$ LANGUAGE plpgsql STRICT IMMUTABLE;
此函数可以使用任何日期来计算未来(diff
的正值)或过去(diff
的负值)的任意天数,包括当前一周。由于它 returns 工作日日期作为标量,因此在您的查询中使用非常简单:
SELECT *
FROM table
WHERE analysis_date >= business_days_diff(current_date, -12);
除此之外,您还可以传入 table 中的字段并执行一些时髦的操作,例如:
SELECT t1.some_value - t2.some_value AS value_diff
FROM table t1
JOIN table t2 ON t2.analysis_date = business_days_diff(t1.analysis_date, -12);
即一定数量的工作日分离的自连接。
请注意,此函数假定工作日为周一至周五。
* 此函数仅对标量值进行简单的算术运算。 CTE 必须设置各种结构来支持迭代和生成的记录集。