sql跳过非工作日(周末和节假日)——转载

本文介绍了一种使用SQL函数来计算基于开始日期和结束日期的工作日工期的方法,包括创建必要的数据表、定义工作日和特殊日历规则,并实现工期计算。
简介:场景1:基于开始日期和工期,推算结束日期。 场景2:基于开始日期和结束日期,计算工期 注:需要自己做界面维护工作日表(s_WorkDay)和节假日表(s_SpecialDay) 

涉及到的数据表
IF OBJECT_ID('s_WorkDay') IS NULL 
BEGIN 
     CREATE TABLE s_WorkDay([Monday] [TINYINT] 
          ,[Tuesday] [TINYINT] 
          ,[Wednesday] [TINYINT] 
          ,[Thursday] [TINYINT] 
          ,[Friday] [TINYINT] 
          ,[Saturday] [TINYINT] 
          ,[Sunday] [TINYINT] 
     )
END
GO

IF OBJECT_ID('s_SpecialDay') IS NULL 
BEGIN 
     CREATE TABLE s_SpecialDay([SpecialDayGUID] [UNIQUEIDENTIFIER] PRIMARY KEY CLUSTERED NOT NULL  
          ,[BeginDate] [DATETIME] 
          ,[EndDate] [DATETIME] 
          ,[IsWorkDay] [TINYINT] 
          ,[Remarks] [VARCHAR](200) 
     )
END
GO
场景1:根据开始日期和工期,计算结束日期
--根据开始日期推出结束日期

IF EXISTS ( SELECT  * FROM    dbo.sysobjects WHERE   id = OBJECT_ID(N'[dbo].[fn_GetEndDate]') AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_GetEndDate]

GO

CREATE   FUNCTION fn_GetEndDate ( @date DATETIME,@Duration INT )

RETURNS DATETIME

AS

    BEGIN

        DECLARE @Edate DATETIME

        DECLARE @IsAdd INT

        SET @Edate=@date

        SET @Duration=@Duration-1

         DECLARE @NoWorkDay TABLE(iDay int)

        --非工作日枚举

        INSERT INTO @NoWorkDay(iDay) SELECT * FROM dbo.fn_NoWorkDay()

       

        WHILE ( @Duration > 0 )

            BEGIN

                SET @IsAdd=0

                --默认往后+1

                SET @Edate=DATEADD(day, 1, @Edate)

                --如果非工作日,重复循环,否则跳下一步

                IF EXISTS( SELECT TOP 1 1 FROM s_workDay WHERE DATEPART(weekday, @Edate) NOT IN (SELECT * FROM @NoWorkDay))

                BEGIN

                 SET @IsAdd=1

                 SET @Duration = @Duration - 1

                END

                --如果当前日期在特殊非工作日中,则不跳

                IF EXISTS( SELECT TOP 1 1 FROM s_SpecialDay WHERE (@Edate BETWEEN begindate AND enddate) AND IsWorkDay=0 AND @IsAdd=1)

                BEGIN

                  SET @Duration = @Duration + 1

                END

                --如果当前日期在特殊工作日中,则跳1

                  IF EXISTS( SELECT TOP 1 1 FROM s_SpecialDay WHERE (@Edate BETWEEN begindate and enddate) AND IsWorkDay=1 AND @IsAdd=0)

                BEGIN

                  SET @Duration = @Duration -1

                END

            END

        RETURN @Edate

    END

GO
场景2:根据两个日期,计算工期
--计算工期

IF EXISTS ( SELECT  * FROM    dbo.sysobjects WHERE   id = OBJECT_ID(N'[dbo].[fn_GetDuration]') AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_GetDuration]

GO

CREATE   FUNCTION fn_GetDuration(@BeginDate DATETIME,@EndDate DATETIME)

RETURNS INT

AS

    BEGIN

        DECLARE @iCount INT

        --A:取出常规工作日

        SELECT  @iCount = ISNULL(dbo.fn_GetWorkDay(@BeginDate, @EndDate), 0) 

       

        --B:减去特殊非工作日

        SELECT  @iCount = @iCount

                - ISNULL(SUM(dbo.fn_GetWorkDay(CASE WHEN begindate < @BeginDate

                                                    THEN @BeginDate

                                                    ELSE begindate

                                               END,

                                               CASE WHEN enddate > @EndDate

                                                    THEN @EndDate

                                                    ELSE enddate

                                               END)), 0)

        FROM    s_SpecialDay

        WHERE   isworkday = 0

                AND SpecialDayGUID NOT IN (

                SELECT  SpecialDayGUID

                FROM    s_SpecialDay

                WHERE   ( begindate > @EndDate

                          AND enddate > @EndDate

                        )

                        OR ( begindate < @BeginDate

                             AND enddate < @BeginDate

                           ) )

     

        ----C:加上特殊工作日

        SELECT  @iCount = @iCount + ISNULL(SUM(DATEDIFF(dd,

                                                        CASE WHEN begindate < @BeginDate

                                                             THEN @BeginDate

                                                             ELSE begindate

                                                        END,

                                                        CASE WHEN enddate > @EndDate

                                                             THEN @EndDate

                                                             ELSE enddate

                                                        END)

                                               - dbo.fn_GetWorkDay(CASE

                                                              WHEN begindate < @BeginDate

                                                              THEN @BeginDate

                                                              ELSE begindate

                                                              END,

                                                              CASE

                                                              WHEN enddate > @EndDate

                                                              THEN @EndDate

                                                              ELSE enddate

                                                              END)), 0)

        FROM    s_SpecialDay

        WHERE   isworkday = 1

                AND SpecialDayGUID NOT IN (

                SELECT  SpecialDayGUID

                FROM    s_SpecialDay

                WHERE   ( begindate > @EndDate

                          AND enddate > @EndDate

                        )

                        OR ( begindate < @BeginDate

                             AND enddate < @BeginDate

                           ) )

        RETURN @iCount

    END

GO
需要用到的函数
IF EXISTS ( SELECT  *

            FROM    dbo.sysobjects

            WHERE   id = OBJECT_ID(N'[dbo].[fn_NoWorkDay]')

                    AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_NoWorkDay]

GO

--输出非工作日

CREATE   FUNCTION fn_NoWorkDay()

RETURNS @NoWorkDay TABLE ( iDay INT )

AS

    BEGIN

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  2

                FROM    s_WorkDay

                WHERE   Monday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  3

                FROM    s_WorkDay

                WHERE   Tuesday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  4

                FROM    s_WorkDay

                WHERE   Wednesday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  5

                FROM    s_WorkDay

                WHERE   Thursday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  6

                FROM    s_WorkDay

                WHERE   Friday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  7

                FROM    s_WorkDay

                WHERE   Saturday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  1

                FROM    s_WorkDay

                WHERE   Sunday = 0

        RETURN 

    END        

 

Go

--计算工作日

IF EXISTS ( SELECT  *

            FROM    dbo.sysobjects

            WHERE   id = OBJECT_ID(N'[dbo].[fn_GetWorkDay]')

                    AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_GetWorkDay]

GO

CREATE   FUNCTION fn_GetWorkDay(@BeginDate DATETIME,@EndDate DATETIME)

RETURNS INT

AS

    BEGIN

        DECLARE @NoWorkDay TABLE(iDay int)

        --非工作日枚举

        INSERT INTO @NoWorkDay(iDay) SELECT * FROM fn_NoWorkDay()

        DECLARE @i INT,@iCount INT

        SET @iCount=0

        SET @i = DATEDIFF(day, @BeginDate, @EndDate) 

        WHILE ( @i >= 0 )

            BEGIN

                --如果开始日期往后顺延,遇到非工作日,则不计数

                IF EXISTS( SELECT TOP 1 1 FROM s_workDay WHERE DATEPART(weekday, @BeginDate) NOT IN (SELECT * FROM @NoWorkDay))

                BEGIN

                 SET @iCount=@iCount+1

                END

                SET @BeginDate=DATEADD(day, 1, @BeginDate)

                SET @i = @i - 1 

            END

        RETURN @iCount

    END

go





#!/usr/bin/python # -*- coding: UTF-8 -*- """ 取引日切替 機能ID:MMT06 """ import os from datetime import datetime from app.business_utils.business_date_utils import ( get_business_date_by_date_type, update_business_date, ) from app.business_utils.calendar_utils import ( calculate_business_day, get_suspension_date, ) from app.business_utils.sql_utils import get_db_column_default, get_sql_dto_by_id from app.dto.base.batch_info_dto import BatchInfoDto from app.dto.base.business_date_dto import BaseDateRecordDto from app.enum.business_date_key_enum import BusinessDateTypeEnum from app.enum.cp_code_enum import CpCodeEnum from app.enum.suspension_target_enum import SuspensionTargetEnum from app.exception.business_exception import BusinessException from app.lib.base_script import BaseScript from app.lib.db_manager import DbManager from app.lib.time_utils import is_weekday #AWS Lambda函数的入口 def lambda_handler(event, context): """ ジョブ実行のHandler """ return BusinessDateExchange().execute(event.get("body")) #AWS Lambda函数的入口 #继承BaseScript class BusinessDateExchange(BaseScript): """ 取引日切替のクラス """ #传递当前的文件路径 def __init__(self): """ 取引日切替のコンストラクタ """ super(BusinessDateExchange, self).__init__( "取引日切替", os.path.basename(__file__) ) return def sub_main(self): """ 取引日切替の主処理 """ # 取引日付リスト self.dates: list[BaseDateRecordDto] = [] # 休日情報取得 self.suspension_dates: list[str] = [] self.__get_suspension_date_info() # 暗号資産用取引日付計算 self.__get_crypto_assets_date_dto() # 共通用取引日付計算 self.__get_default_date_dto() try: # トランザクションマネジャーインスタンス DbManager.start_transaction() for date_dto in self.dates: # 取引日付テーブルの更新 update_business_date( data_type=date_dto.data_type, last_business_date=date_dto.last_business_date, base_business_date=date_dto.base_business_date, next_business_date=date_dto.next_business_date, update_dt=self.server_system_time, ) if date_dto.data_type == BusinessDateTypeEnum.CRYPTO_ASSETS.value: # 暗号資産の場合 # B2C2のLP取込ステータス情報の登録 self.__register_lp_import_status( date_dto.last_business_date, CpCodeEnum.B2C2.value, ) # VCTのLP取込ステータス情報の登録 self.__register_lp_import_status( date_dto.last_business_date, CpCodeEnum.VCT.value, ) DbManager.commit() self.logger.info("I100018") except Exception as e: # SQL実施失敗の場合、ロールバックする DbManager.rollback() raise e finally: DbManager.close_transaction() return def __register_lp_import_status(self, last_business_date, cp_code): """ LP取込ステータス情報を登録する """ params = { "base_date": last_business_date, "cp_code": cp_code, "import_status": 0, } # データベースのデフォルトカラムを取得する。 db_column_default = get_db_column_default(self.server_system_time) params.update(db_column_default) sql_dto = get_sql_dto_by_id("mmt06.insertLpImportStatus", params) DbManager.execute_sql(sql_dto=sql_dto, is_auto_commit=False) return def __get_suspension_date_info(self) -> list[str]: """ 休日情報を取得する """ records = get_suspension_date(SuspensionTargetEnum.ALL.value) self.suspension_dates = [(item.get("suspension_date")) for item in records] return def __get_default_date_dto(self): """ 区分が10(ディフォルト)の前取引日、取引日、翌取引日を計算して返却する """ # 千手日付が土曜日、日曜日の判定 is_weekend = not is_weekday( datetime.strptime(BatchInfoDto.senju_date, "%Y%m%d") ) if is_weekend: # 千手日付が土曜日、日曜日の場合、ディフォルトの取引日付を更新しない return None result = get_business_date_by_date_type(BusinessDateTypeEnum.DEFAULT) if result is None: raise BusinessException("E200007", "取引日付テーブルから共通用データ") date_dto = BaseDateRecordDto() date_dto.data_type = BusinessDateTypeEnum.DEFAULT.value # 取引日=取引日付テーブル.翌取引日 date_dto.base_business_date = result.next_business_date # 前取引日=取引日付テーブル.取引日 date_dto.last_business_date = result.base_business_date # 翌取引日を計算する # date_dto.next_business_date = self.__calculate_next_business_date( # base_date=date_dto.base_business_date, need_weekend=True # ) date_dto.next_business_date = calculate_business_day( base_date=date_dto.base_business_date, suspension_dates=self.suspension_dates, is_pre_business_date=False, need_weekend_judgment=True, ) self.dates.append(date_dto) return def __get_crypto_assets_date_dto(self): """ 区分が20(暗号資産)の前取引日、取引日、翌取引日を計算して返却する """ result = get_business_date_by_date_type(BusinessDateTypeEnum.CRYPTO_ASSETS) if result is None: raise BusinessException("E200007", "取引日付テーブルから暗号資産用データ") date_dto = BaseDateRecordDto() # 区分 date_dto.data_type = BusinessDateTypeEnum.CRYPTO_ASSETS.value # 前取引日=取引日付テーブル.取引日 date_dto.last_business_date = result.base_business_date # 取引日=取引日付テーブル.翌取引日 date_dto.base_business_date = result.next_business_date # 翌取引日を計算する date_dto.next_business_date = calculate_business_day( base_date=date_dto.base_business_date, suspension_dates=self.suspension_dates, is_pre_business_date=False, need_weekend_judgment=False, ) self.dates.append(date_dto) return
05-27
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值