SQL 计算 20 个工作日,删除银行存款并引用另一个查询

Posted

技术标签:

【中文标题】SQL 计算 20 个工作日,删除银行存款并引用另一个查询【英文标题】:SQL Calculate 20 business days, remove bank hols and reference to another query 【发布时间】:2017-01-11 17:33:16 【问题描述】:

抱歉,我对在 SQL Server 2008 R2 中创建函数很陌生。我基本上可以通过使用 T-SQL 语句来获得。

但是,我需要创建一个返回带有日期(程序开始日期)的记录的报告,这部分很简单,但是对于每个行日期,我想根据 20 个工作日计算目标完成日期。我也想避免计算银行假期。我有一个名为 dCalendar 的表,它在过去几年中每天都保存着标记,表明每一天是工作日还是银行假日。

我找到了很多关于如何计算两个日期之间的工作日数的资料,但这更棘手。

我已经创建了这个函数

ALTER function [warehouse].[MS_fnAddBusinessDays] 
    (@StartDate datetime,
     @nDays int) 
RETURNS TABLE
AS 
    RETURN 
        (SELECT calDt 
         FROM 
             (SELECT
                  ROW_NUMBER() OVER (ORDER BY calDt ASC) AS rownumber, 
                  calDt
              FROM 
                  warehouse.dCalendar
              WHERE 
                  (calDt >= @StartDate) 
                  AND (weekDayFg = 1) 
                  AND (BankHolidayFg = 0)) AS Results
    WHERE 
        (rownumber = @nDays) 

并且可以使用以下方式调用它

SELECT * 
FROM 
    (SELECT  
         ROW_NUMBER() OVER (ORDER BY calDt ASC) AS rownumber, 
         calDt, BankHolidayFg, weekDayFg, dateStr 
     FROM 
         warehouse.dCalendar 
     WHERE 
         (calDt >= CONVERT(DATETIME, '2016-12-11 00:00:00', 102)) 
         AND (weekDayFg = 1) AND (BankHolidayFg = 0)) AS TblResults 
WHERE 
    (rownumber = 20) 

我只是无法弄清楚如何在以下示例中嵌入它,其中 progStartDate 是我想要计算目标日期的日期

SELECT     
    end_user.fContactVwDn.client_no, 
    end_user.fProgrammeVwDn.prtyProgrammeType, 
    end_user.fProgrammeVwDn.progStartDate, 
    SUM(CASE WHEN comtContactMeetingType = 'Visit' THEN 1 ELSE 0 END) AS InitialVisitTotal, 
    MAX(CASE WHEN comtContactMeetingType = 'Visit' THEN contPlannedFromDate ELSE 0 END) AS InitialVisitDate
FROM
    end_user.fContactVwDn 
INNER JOIN
    warehouse.fContactProgramme ON end_user.fContactVwDn.contKy = warehouse.fContactProgramme.contKy 
INNER JOIN
    end_user.fProgrammeVwDn ON warehouse.fContactProgramme.progGuid = end_user.fProgrammeVwDn.progprogGuid
GROUP BY 
    end_user.fProgrammeVwDn.prtyProgrammeType, 
    end_user.fProgrammeVwDn.progStartDate, end_user.fContactVwDn.client_no
HAVING      
    (end_user.fProgrammeVwDn.prtyProgrammeType = 'Application') 
    AND (end_user.fProgrammeVwDn.progStartDate > CONVERT(DATETIME, '2016-07-01 00:00:00', 102))

任何帮助将不胜感激。

【问题讨论】:

"并且可以使用以下命令调用它" @nDays 参数? 【参考方案1】:

该功能看起来不错。不过,我会添加TOP,以避免扫描整个Calendar 表。我希望calDt 是主键。

ALTER function [warehouse].[MS_fnAddBusinessDays] 
    (@StartDate datetime,
    @nDays int) 
RETURNS TABLE
AS 
RETURN 
(
    SELECT calDt 
    FROM 
        (
            SELECT TOP(@nDays)
                ROW_NUMBER() OVER (ORDER BY calDt ASC) AS rownumber, 
                calDt
            FROM 
                warehouse.dCalendar
            WHERE 
                (calDt >= @StartDate) 
                AND (weekDayFg = 1) 
                AND (BankHolidayFg = 0)
            ORDER BY calDt ASC
        ) AS Results
    WHERE 
        rownumber = @nDays
)

这个函数是内联表值函数,这意味着它返回一个表,而不是一个标量值。这很好,因为标量函数通常会使查询变慢,但内联(单语句)表值函数可以由优化器内联。

要调用此类函数,请使用CROSS APPLY。它最初被引入到 SQL Server 专门用于调用表值函数,但它可以做much more(即所谓的横向连接)。

我将您的原始查询包装在 CTE 中,以使最终查询可读。

WITH
CTE
AS
(
    SELECT
        end_user.fContactVwDn.client_no, 
        end_user.fProgrammeVwDn.prtyProgrammeType, 
        end_user.fProgrammeVwDn.progStartDate, 
        SUM(CASE WHEN comtContactMeetingType = 'Visit' THEN 1 ELSE 0 END) AS InitialVisitTotal, 
        MAX(CASE WHEN comtContactMeetingType = 'Visit' THEN contPlannedFromDate ELSE 0 END) AS InitialVisitDate
    FROM
        end_user.fContactVwDn 
        INNER JOIN warehouse.fContactProgramme ON end_user.fContactVwDn.contKy = warehouse.fContactProgramme.contKy 
        INNER JOIN end_user.fProgrammeVwDn ON warehouse.fContactProgramme.progGuid = end_user.fProgrammeVwDn.progprogGuid
    GROUP BY 
        end_user.fProgrammeVwDn.prtyProgrammeType, 
        end_user.fProgrammeVwDn.progStartDate, 
        end_user.fContactVwDn.client_no
    HAVING      
        (end_user.fProgrammeVwDn.prtyProgrammeType = 'Application') 
        AND (end_user.fProgrammeVwDn.progStartDate > CONVERT(DATETIME, '2016-07-01 00:00:00', 102))
)
SELECT
    CTE.client_no, 
    CTE.prtyProgrammeType, 
    CTE.progStartDate, 
    CTE.InitialVisitTotal, 
    CTE.InitialVisitDate,
    F.calDt
FROM
    CTE
    CROSS APPLY [warehouse].[MS_fnAddBusinessDays](CTE.progStartDate, 20) AS F
;

【讨论】:

以上是关于SQL 计算 20 个工作日,删除银行存款并引用另一个查询的主要内容,如果未能解决你的问题,请参考以下文章

习题 2 任选两道

something

软件工程 作业2

c#0银行存款计算器

编写一个类似银行账户的程序,属性:账号 储户姓名 地址 存款余额 利率。方法:存款 取款查询余额计算利息

homework2.2