Amazon Redshift 本月剩余工作日
Remaining Business Days This Month in Amazon Redshift
我想找到一种方法来使用 Redshift 确定这个日历月的剩余工作日数。我目前有一个朋友写的 MySQL 版本。我不太了解它是如何写的,甚至无法翻译成另一种方言。但是,如果有人可以帮助翻译它,这将是一个非常有用的工具!
输出的功能应该与 excel 中的 networkdays()
功能一样。在此函数中,提供了 begin_date 和 end_date 作为函数的参数。它计算开始日期和结束日期之间的工作日数(非周末日历天数)。
这里是当前MySQL:
SELECT 1 AS pk ,COUNT(*) AS remaining
FROM (
SELECT WEEKDAY(DATE(DATE_FORMAT(CONVERT_TZ(NOW(), 'UTC', 'PST8PDT'), '%Y-%m-01') + INTERVAL (a.num-1) DAY)) AS weekdays
FROM (
SELECT @row := @row + 1 AS num
FROM schema.table t, (SELECT @row := 0) r
) a
WHERE a.num >= DAY(CONVERT_TZ(NOW(), 'UTC', 'PST8PDT'))
AND a.num <= DAY((DATE_FORMAT(CONVERT_TZ(NOW(), 'UTC', 'PST8PDT'), '%Y-%m-01') + INTERVAL 1 MONTH) - INTERVAL 1 DAY)
) b
WHERE b.weekdays NOT IN (0,6)
任何帮助都会很棒!
为了计算工作日数,您需要使用 date_trunc()
function. After that, you need to get a number of days for particular month (month_last_day
) using extract()
function,there is a wiki page regarding this. And finally, you can generate_series()
of days using start_date
date and month_last_day
number excluding the weekend days using date_part()
函数。
CREATE OR REPLACE FUNCTION extract_month_business_days(d DATE, count_remaining BOOLEAN)
RETURNS INTEGER AS $$
DECLARE
start_date DATE;
month_last_day INTEGER;
result INTEGER;
BEGIN
IF count_remaining THEN
start_date = d;
ELSE
start_date = date_trunc('month',d);
END IF;
month_last_day = extract(DAY FROM date_trunc('month',d) + INTERVAL '1 MONTH - 1 day');
SELECT count(*) INTO result FROM generate_series(0,(month_last_day - extract(DAY FROM start_date))::INTEGER) day
WHERE date_part('dow', start_date + day) NOT IN (0,6);
RETURN result;
END;
$$ LANGUAGE plpgsql;
结果:
WITH t(dates) AS ( VALUES
('2016-02-18'::DATE),
('2016-03-18'::DATE),
('2016-04-18'::DATE),
('2016-05-18'::DATE)
)
SELECT
to_char(dates,'Month YY') AS month,
extract_month_business_days(dates,FALSE) AS number_business_days,
extract_month_business_days(dates,TRUE) AS remaining_business_days
FROM t;
month | number_business_days | remaining_business_days
--------------+----------------------+-------------------------
February 16 | 21 | 8
March 16 | 23 | 10
April 16 | 21 | 10
May 16 | 22 | 10
(4 rows)
更新 - REDSHIFT 版本
正如@John指出的那样,generate_series()
在AWS Redshift中不可用,函数定义如下:
CREATE OR REPLACE FUNCTION extract_month_business_days(d DATE, count_remaining BOOLEAN)
RETURNS INTEGER AS $$
DECLARE
start_date DATE;
month_last_day INTEGER;
result INTEGER;
i INTEGER;
BEGIN
result = 0;
IF count_remaining THEN
start_date = d;
ELSE
start_date = date_trunc('month',d);
END IF;
month_last_day = extract(DAY FROM date_trunc('month',d) + INTERVAL '1 MONTH - 1 day');
result = 0;
FOR i IN 0..(month_last_day - extract(DAY FROM start_date))::INTEGER LOOP
IF (date_part('dow', start_date + i) NOT IN (0,6)) THEN
result = result + 1;
END IF;
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
不需要函数,你可以用一个 SQL 语句来完成:
SELECT count(*)
FROM generate_series(CURRENT_TIME,
date_trunc('month', CURRENT_TIME) + interval '1 month - 1 day',
interval '1 day') days(d)
WHERE extract(dow from d) NOT IN (0, 6);
当然,如果需要,您可以将其包装在 SQL 函数中。考虑到您要求当月剩余工作天数,您不需要指定任何参数。
数据仓库的一个流行想法是创建一个 calendar
table 包含所有日期(或至少与公司相关的日期)以及标志,例如:
- Public 假期
- 工作日
- 每月的第一个(工作)日
- 一个月的最后一个(工作)日
- 月数
- 周数
- 天数
虽然其中许多值都可以通过日期函数计算,但加入 calendar
table 以执行某些日期函数通常会更容易。
在计算剩余工作日的情况下,只需计算 calendar
table 中在所需范围内的行数,其中 is_work_day
标志已设置。这可以通过 JOIN
或子查询来完成。
不像其中一些查询那么花哨,但通常更容易维护。此外,Amazon Redshift 不支持 generate_series
函数,因此通常都是可以的。
另请参阅:
- 堆栈溢出Calendar table for Data Warehouse
- MSQL 技巧Creating a date dimension or calendar table in SQL Server
我的回答是 table 只进行大量交易,至少在您关心的月份每天一次。对我来说幸运的是,我们的系统允许用户在未来安排交易,所以我可以通过 ping 来获取一些简单的日期逻辑。
SELECT
count(CASE WHEN business_day < date(getdate()) THEN 1 END) as passed
,count(business_day) as total_business_days
FROM
(SELECT distinct
date(o.appointment_full_time) as business_day
FROM
orders o
WHERE
date_trunc('month', o.appointment_full_time) = date_trunc('month', getdate())
--this month
AND extract(dow from o.appointment_full_time) not in (0,6)
--exclude weekends
AND date(o.appointment_full_time)
not in ('2017-1-1', '2017-1-2', '2017-1-16', '2017-5-29', '2017-7-4', '2017-9-4',
'2017-11-23', '2017-11-24', '2017-12-25', '2017-12-24', '2017-12-31')
--manually enter the holidays in once per year
) a
我想找到一种方法来使用 Redshift 确定这个日历月的剩余工作日数。我目前有一个朋友写的 MySQL 版本。我不太了解它是如何写的,甚至无法翻译成另一种方言。但是,如果有人可以帮助翻译它,这将是一个非常有用的工具!
输出的功能应该与 excel 中的 networkdays()
功能一样。在此函数中,提供了 begin_date 和 end_date 作为函数的参数。它计算开始日期和结束日期之间的工作日数(非周末日历天数)。
这里是当前MySQL:
SELECT 1 AS pk ,COUNT(*) AS remaining
FROM (
SELECT WEEKDAY(DATE(DATE_FORMAT(CONVERT_TZ(NOW(), 'UTC', 'PST8PDT'), '%Y-%m-01') + INTERVAL (a.num-1) DAY)) AS weekdays
FROM (
SELECT @row := @row + 1 AS num
FROM schema.table t, (SELECT @row := 0) r
) a
WHERE a.num >= DAY(CONVERT_TZ(NOW(), 'UTC', 'PST8PDT'))
AND a.num <= DAY((DATE_FORMAT(CONVERT_TZ(NOW(), 'UTC', 'PST8PDT'), '%Y-%m-01') + INTERVAL 1 MONTH) - INTERVAL 1 DAY)
) b
WHERE b.weekdays NOT IN (0,6)
任何帮助都会很棒!
为了计算工作日数,您需要使用 date_trunc()
function. After that, you need to get a number of days for particular month (month_last_day
) using extract()
function,there is a wiki page regarding this. And finally, you can generate_series()
of days using start_date
date and month_last_day
number excluding the weekend days using date_part()
函数。
CREATE OR REPLACE FUNCTION extract_month_business_days(d DATE, count_remaining BOOLEAN)
RETURNS INTEGER AS $$
DECLARE
start_date DATE;
month_last_day INTEGER;
result INTEGER;
BEGIN
IF count_remaining THEN
start_date = d;
ELSE
start_date = date_trunc('month',d);
END IF;
month_last_day = extract(DAY FROM date_trunc('month',d) + INTERVAL '1 MONTH - 1 day');
SELECT count(*) INTO result FROM generate_series(0,(month_last_day - extract(DAY FROM start_date))::INTEGER) day
WHERE date_part('dow', start_date + day) NOT IN (0,6);
RETURN result;
END;
$$ LANGUAGE plpgsql;
结果:
WITH t(dates) AS ( VALUES
('2016-02-18'::DATE),
('2016-03-18'::DATE),
('2016-04-18'::DATE),
('2016-05-18'::DATE)
)
SELECT
to_char(dates,'Month YY') AS month,
extract_month_business_days(dates,FALSE) AS number_business_days,
extract_month_business_days(dates,TRUE) AS remaining_business_days
FROM t;
month | number_business_days | remaining_business_days
--------------+----------------------+-------------------------
February 16 | 21 | 8
March 16 | 23 | 10
April 16 | 21 | 10
May 16 | 22 | 10
(4 rows)
更新 - REDSHIFT 版本
正如@John指出的那样,generate_series()
在AWS Redshift中不可用,函数定义如下:
CREATE OR REPLACE FUNCTION extract_month_business_days(d DATE, count_remaining BOOLEAN)
RETURNS INTEGER AS $$
DECLARE
start_date DATE;
month_last_day INTEGER;
result INTEGER;
i INTEGER;
BEGIN
result = 0;
IF count_remaining THEN
start_date = d;
ELSE
start_date = date_trunc('month',d);
END IF;
month_last_day = extract(DAY FROM date_trunc('month',d) + INTERVAL '1 MONTH - 1 day');
result = 0;
FOR i IN 0..(month_last_day - extract(DAY FROM start_date))::INTEGER LOOP
IF (date_part('dow', start_date + i) NOT IN (0,6)) THEN
result = result + 1;
END IF;
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
不需要函数,你可以用一个 SQL 语句来完成:
SELECT count(*)
FROM generate_series(CURRENT_TIME,
date_trunc('month', CURRENT_TIME) + interval '1 month - 1 day',
interval '1 day') days(d)
WHERE extract(dow from d) NOT IN (0, 6);
当然,如果需要,您可以将其包装在 SQL 函数中。考虑到您要求当月剩余工作天数,您不需要指定任何参数。
数据仓库的一个流行想法是创建一个 calendar
table 包含所有日期(或至少与公司相关的日期)以及标志,例如:
- Public 假期
- 工作日
- 每月的第一个(工作)日
- 一个月的最后一个(工作)日
- 月数
- 周数
- 天数
虽然其中许多值都可以通过日期函数计算,但加入 calendar
table 以执行某些日期函数通常会更容易。
在计算剩余工作日的情况下,只需计算 calendar
table 中在所需范围内的行数,其中 is_work_day
标志已设置。这可以通过 JOIN
或子查询来完成。
不像其中一些查询那么花哨,但通常更容易维护。此外,Amazon Redshift 不支持 generate_series
函数,因此通常都是可以的。
另请参阅:
- 堆栈溢出Calendar table for Data Warehouse
- MSQL 技巧Creating a date dimension or calendar table in SQL Server
我的回答是 table 只进行大量交易,至少在您关心的月份每天一次。对我来说幸运的是,我们的系统允许用户在未来安排交易,所以我可以通过 ping 来获取一些简单的日期逻辑。
SELECT
count(CASE WHEN business_day < date(getdate()) THEN 1 END) as passed
,count(business_day) as total_business_days
FROM
(SELECT distinct
date(o.appointment_full_time) as business_day
FROM
orders o
WHERE
date_trunc('month', o.appointment_full_time) = date_trunc('month', getdate())
--this month
AND extract(dow from o.appointment_full_time) not in (0,6)
--exclude weekends
AND date(o.appointment_full_time)
not in ('2017-1-1', '2017-1-2', '2017-1-16', '2017-5-29', '2017-7-4', '2017-9-4',
'2017-11-23', '2017-11-24', '2017-12-25', '2017-12-24', '2017-12-31')
--manually enter the holidays in once per year
) a