【问题标题】:Remaining Business Days This Month in Amazon RedshiftAmazon Redshift PostgreSQL 本月剩余工作日
【发布时间】:2016-07-05 20:32:03
【问题描述】:

我想找到一种方法来使用 Redshift PostgreSQL 确定这个日历月的剩余工作日数。我目前有一个朋友写的 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)

任何帮助都会很棒!

【问题讨论】:

标签: mysql postgresql datetime amazon-redshift date-manipulation


【解决方案1】:

数据仓库的一个流行想法是创建一个calendar 表,其中包含所有日期(或至少与公司相关的日期)以及以下标志:

  • 公众假期
  • 工作日
  • 每月的第一天(工作)
  • 一个月的最后一个(工作)日
  • 月数
  • 周数
  • 天数

虽然其中许多值可以通过日期函数计算,但加入calendar 表以执行某些日期函数通常更容易。

在计算剩余工作日的情况下,只需计算calendar 表中位于所需范围内的行数,其中设置了is_work_day 标志。这可以通过JOIN 或子查询来完成。

不像其中一些查询那么花哨,但通常更容易维护。此外,Amazon Redshift 不支持 generate_series 函数,因此通常只能这样做。

另见:

【讨论】:

    【解决方案2】:

    不需要函数,只需一条 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 函数中。考虑到您对当月剩余工作日的需求,您无需指定任何参数。

    【讨论】:

    • 很遗憾,generate_series 在 Amazon Redshift 下不受支持。
    • @JohnRotenstein 啊,对。如果我没记错的话,PG 8.0。那太老了(EOL 5 年多之后),而且缺乏我们都喜欢和使用的功能,至少从 8.4 开始,它仍然被称为 PostgreSQL。
    • 嗯,Redshift SQL 前端的原始代码库是 8.0.6,但他们已经用诸如 WINDOW 函数之类的东西对其进行了更新。因此,它是版本的混合体,以及针对特定于 Redshift 的用例的附加命令。
    • @JohnRotenstein 因此为什么 Redshift 真的不再是 PostgreSQL 了。
    【解决方案3】:

    为了获得工作日的计数,您需要使用 date_trunc() 函数知道该月的第一天的日期 (start_date)。之后,您需要使用extract() 函数获取特定月份的天数 (month_last_day),对此有一个wiki page。最后,您可以使用start_date 日期和month_last_day 数字generate_series() 天数排除使用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)
    

    更新 - 红移版

    正如 @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;
    

    【讨论】:

    • 很遗憾,generate_series 在 Amazon Redshift 下不受支持。
    • 我喜欢你的代码!不幸的是,Redshift 只有supports functions in Python。另一种方法可能是使用 WINDOW 函数以某种方式生成系列。
    【解决方案4】:

    我的回答是在一张桌子上只放一大堆交易,在您关心的月份中每天至少有一个交易。对我来说幸运的是,我们的系统允许用户安排未来的交易,所以我可以 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
    

    【讨论】:

      猜你喜欢
      • 2014-07-28
      • 1970-01-01
      • 1970-01-01
      • 2021-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多