确定如何在SQL Server中计算拆分周数

时间:2010-09-24 21:38:31

标签: sql-server-2005 datetime

我不太确定这个术语是什么,我一直称它为“Split Weeks”,但这是我需要找到的。

假设:

用户将输入@StartDate@EndDate

col_week_end_date将始终在星期六结束,并且是DateTime列。

我希望循环多个月或一个月,并总结col_payment_amt

使用2010年9月的月份,col_payment_amt col_week_end_date落在09/04/2010,涵盖8月29日至9月4日这一周。

付款月份是九月,但本周(周三,周四,周五)只有3个工作日。因此,该周只支付了3/5的款项。

同样的事情发生在一个月末。在这种情况下,col_week_end_date将于2010年2月10日发布。本周仅支付4/5的款项。

我有一种特殊的方法可以在一个月初和最后发生这种情况时对col_payment_amt求和。

我无法弄清楚的是如何告诉我何时在一个月的开始,当我在一个月结束时所以我可以应用适当的功能,在运行报告时多个月(8月 - 10月)。

目前,如果我只是强迫他们运行一个月的报告,没有问题,我被告知这只是一个月度报告,但我知道最终我会被问到是否有可以运行多个月。

我知道它基本上是这样的:

SELECT sum(CASE WHEN at-top-of-month-with-split-week THEN....
                WHEN at-bottom-of-month-with-split-week THEN...
                ELSE col_payment_amt END) as PayTotal
FROM...
WHERE....
GROUP BY...

我正试图弄清楚月末和月底部分。我有其他部分。

2 个答案:

答案 0 :(得分:2)

此代码适用于IBM Informix Dynamic Server(在11.50.FC6上针对MacOS X 1.06.4进行了测试,但适用于任何受支持的IDS版本以及任何平台)。您需要转换为MS SQL Server表示法。

版本2 - 仅按月号标识的参考月

CREATE FUNCTION NumWeekDaysInRefMonth(eow DATE DEFAULT TODAY, ref_month INTEGER)
    RETURNING INT AS numdays;
    DEFINE monday DATE;
    DEFINE friday DATE;
    DEFINE mon_month INTEGER;
    DEFINE fri_month INTEGER;

    IF eow IS NULL OR ref_month IS NULL THEN RETURN NULL; END IF;

    LET monday = eow - WEEKDAY(eow) + 1;
    LET friday = monday + 4;
    LET mon_month = MONTH(monday);
    LET fri_month = MONTH(friday);

    IF mon_month = ref_month AND fri_month = ref_month THEN
        -- All in same month: 5 days count.
        RETURN 5;
    END IF;
    IF mon_month != ref_month AND fri_month != ref_month THEN
        -- None in the same month: 0 days count.
        RETURN 0;
    END IF;
    -- Some of the days are in the same month, some are not.
    IF mon_month = ref_month THEN
        -- End of month
        RETURN 5 - DAY(friday);
    ELSE
        -- Start of month
        RETURN DAY(friday);
    END IF;

END FUNCTION;

测试用例

SELECT NumWeekDaysInRefMonth('2010-09-04',  9) answer, 3 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-09-04',  8) answer, 2 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-02',  9) answer, 4 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-02', 10) answer, 1 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-02',  8) answer, 0 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-09', 10) answer, 5 AS expected FROM dual;

这与前一版本(下面)的逻辑大致相同;只需一个月的数字即可确定您感兴趣的结算月份,而非完整日期。

版本1 - 完整参考日期

CREATE FUNCTION NumWeekDaysInRefMonth(eow DATE DEFAULT TODAY, ref DATE DEFAULT TODAY)
    RETURNING INT AS numdays;
    DEFINE mon DATE;
    DEFINE fri DATE;
    DEFINE v_mon INTEGER;
    DEFINE v_fri INTEGER;
    DEFINE v_ref INTEGER;

    IF eow IS NULL OR ref IS NULL THEN RETURN NULL; END IF;

    LET mon = eow - WEEKDAY(eow) + 1;
    LET fri = mon + 4;
    LET v_mon = YEAR(mon) * 100 + MONTH(mon);
    LET v_fri = YEAR(fri) * 100 + MONTH(fri);
    LET v_ref = YEAR(ref) * 100 + MONTH(ref);

    IF v_mon = v_ref AND v_fri = v_ref THEN
        -- All in same month: 5 days count.
        RETURN 5;
    END IF;
    IF v_mon != v_ref AND v_fri != v_ref THEN
        -- None in the same month: 0 days count.
        RETURN 0;
    END IF;
    -- Some of the days are in the same month, some are not.
    IF v_mon = v_ref THEN
        -- End of month
        RETURN 5 - DAY(fri);
    ELSE
        -- Start of month
        RETURN DAY(fri);
    END IF;

    -- Month-end wrapping
    -- 26 27 28 29 30 31  1  2  3  4  5  6  Jan, Mar, May, Jul, Aug, Oct, Dec
    -- 25 26 27 28 29 30  1  2  3  4  5  6  Apr, Jun, Sep, Nov
    -- 24 25 26 27 28 29  1  2  3  4  5  6  Feb - leap year
    -- 23 24 25 26 27 28  1  2  3  4  5  6  Feb
    -- Mo Tu We Th Fr Sa Su Mo Tu We Th Fr
    -- Su Mo Tu We Th Fr Sa Su Mo Tu We Tu
    -- Sa Su Mo Tu We Th Fr Sa Su Mo Tu We
    -- Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu
    -- Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo
    -- We Th Fr Sa Su Mo Tu We Th Fr Sa Su
    -- Tu We Th Fr Sa Su Mo Tu We Th Fr Sa

END FUNCTION;

测试用例

这些测试与之前的测试相同。

SELECT NumWeekDaysInRefMonth('2010-09-04', '2010-09-01') answer, 3 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-09-04', '2010-08-01') answer, 2 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-02', '2010-09-01') answer, 4 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-02', '2010-10-01') answer, 1 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-02', '2010-08-01') answer, 0 AS expected FROM dual;
SELECT NumWeekDaysInRefMonth('2010-10-09', '2010-10-01') answer, 5 AS expected FROM dual;

解释

Informix DATE类型以天为单位进行计数,因此在DATE中添加1即可获得后一天。 Informix WEEKDAY函数返回0表示星期日,1表示星期一,...,5表示星期五,6表示星期六。 DAY,MONTH和YEAR函数返回DATE值的相应分量。

该代码允许将一周中的任何一天作为付款的参考日期(它不必是星期六)。同样,虽然版本1的示例使用月中的第一天作为月份的参考日,但您可以提供必要月份内的任何日期作为参考日期;在版本2中,简化为传递必需的月份数。

如果有人将NULL传递给函数,则答案为NULL。

然后我们计算包含“周末”日期的星期一;如果工作日是星期日,则减去0并加1以获得星期一;如果工作日是星期六,它减去6并加1以获得星期一;周五是4天后。

在版本1中,我们计算年和月的表示。

如果周一和周五都在参考月份,那么答案是5天;如果两者都没有落入参考月份,答案是0天。如果星期五在参考月份内,则星期五日期的DAY()值是该月份的天数。否则,星期一在参考月份内,并且该月份的天数是5天(星期五)。

请注意,此计算涉及完全无关的月份 - 如最后一个测试用例所示; 10月付款的零天应该在8月份计算。

答案 1 :(得分:1)

好的,这就是我最终做的事情。

我设置了一个WHILE循环。我遍历日期范围的月份,并将数据插入TempTable以及数字引用月份列(嗯,实际上是INT)和月份名称(9月,10月等...)。然后从TempTable I中选择数据并使用CASE语句确定要使用的计算。所以它读到这样的东西......

                -- Top of the month, Split Week
select sum(case when datepart(d,col_week_end_date) <= 7 AND RefMonth = month(col_ween_end_date)
                then ...Top of Month calc...

                -- Middle of the month, entire week is in Reference Month
                when datepart(d,col_week_end_date) > 7 AND RefMonth = month(col_week_end_date)
                then ...Just SUM the column as usual...

                -- Bottom of month, Split Week
                when RefMonth < month(col_week_end_date)
                then ...Bottom of Month calc...
          ) end as MonthlySum,
       col_RefMonth,
       col_RefMonthName
from dbo.TempTable
group by col_RefMonth, col_RefMonthName
order by col_RefMonth

到目前为止,由于我拥有的数据量有限,它似乎与我可用的月份中的分周数一起工作。似乎“参考月”是关键,我需要循环并将数据放入TempTable中。我希望有一种方法可以在一次传递中获得它,或者至少快速转储到#TempTable并从中进行选择。

我不确定它的效率如何,但它只适用于月度报告,而不是真正的在线互动报告。