【问题标题】:Force a date to the first day of the month inside a stored procedure在存储过程中将日期强制为每月的第一天
【发布时间】:2021-10-22 01:01:39
【问题描述】:

在 SE 上有一些使用 DateAdd 和 DatePart 的示例,但它们都没有解决我的具体问题,即如何使用参数执行此操作。我需要做的是强制日期参数使用当月的第一天,而不是传递给参数的日期。所以,我截断的代码如下所示:

ALTER PROCEDURE [dbo].[usp_CR_PW_Insert]
(
    @PR_STATUS int = NULL 
    ,@CASE_TYPE int = NULL
    ,@FROM_DATE date = NULL
    ,@TO_DATE date = NULL
        
)

AS
BEGIN

    BEGIN TRY
      BEGIN TRANSACTION;

        INSERT into [dbo].[tbl_CR_Premium]
        (
        PR_STATUS 
        ,CASE_TYPE
        ,FROM_DATE
        ,TO_DATE
        )
      
        VALUES 
        (
        @PR_STATUS
        ,@CASE_TYPE
        ,@FROM_DATE
        ,@TO_DATE
        );

        COMMIT TRANSACTION; 
    END TRY

    BEGIN CATCH
        BEGIN  
        PRINT  
            N'The transaction is in an uncommittable state.' +  
            'Rolling back transaction.'  
        ROLLBACK TRANSACTION;  
        END;  
    END CATCH;


END

GO

我正在使用日期选择器来获取日期,但我们只想存储所选月份的第一个日期。因此,如果 @FROM_DATE = 4/8/2021 我们实际上想将 4/1/2021 写入表中。我该怎么做?

【问题讨论】:

  • 单个语句是原子的——你不需要在这里进行事务。您也不应该以这种方式使用 PRINT。通过不处理错误或重新抛出错误,让此过程的调用者知道发生了错误。您的代码有效地吃掉了错误,任何人或任何其他代码都无法确切知道错误发生的原因。并停止提供无意义的默认值 - 不要鼓励懒惰的编码
  • 要添加到@SMor 所说的内容,请完全删除TRY\CATCH 并使用SET XACT ABORT ON;,这样可以确保回滚
  • 为简单起见,代码被截断。我不是在寻找编码建议,而是在寻找问题的答案。

标签: sql sql-server


【解决方案1】:
SELECT DATEADD(month, DATEDIFF(month, 0, @FROM_DATE), 0) AS StartOfMonth

这应该可行。

因此您可以在存储过程的开始处执行此操作。

SET @FROM_DATE = DATEADD(month, DATEDIFF(month, 0, @FROM_DATE), 0)

然后正常运行你的代码。

【讨论】:

    【解决方案2】:

    您可以使用一些锚定日期将日期“截断”到该月的第一天。例如,2000 年 1 月 1 日。

    SET @FROM_DATE =
        DATEADD(month, DATEDIFF(month, '2000-01-01', @FROM_DATE), '2000-01-01')
    

    这适用于任何类型:datedatetimedatetime2

    之所以有效,是因为DATEDIFF 返回在指定开始日期和结束日期之间交叉的指定日期部分边界的计数。例如,DATEDIFF(month, '2000-01-31', '2000-02-01') 返回 1。

    【讨论】:

      【解决方案3】:

      DATEFROMPARTS 通常是这样做的最佳方式:

      SET @FROM_DATE = DATEFROMPARTS(YEAR(@FROM_DATE), MONTH(@FROM_DATE), 1);
      

      【讨论】:

      • 在稳定性或可用性方面,您如何将其与 Vladimir 的答案进行比较?老实说,弗拉基米尔的回答效果很好,但这感觉更好,因为它不像他基于 2000 年那样基于任何年份。
      • @JohnnyBones, DATEFROMPARTS 是在 SQL Server 2012 中添加的。不过,这仅对仍在使用以前版本的人很重要。此外,DATEADD / DATEDIFF 方法可用于将date/time 截断为任何单位:年、月、周、日。
      • @VladimirBaranov 正如DATEFROMPARTS/DATETIMEFROMPARTS。无论如何,使用 SQL Server 2008 或更早版本的任何人都需要检查头部
      • @JohnnyBones 对于每个人来说,我认为这更清楚。
      【解决方案4】:

      迟到的答案,只是因为我不认为这是一种选择。

      由于只会解决一次,所以应该考虑format()

      Set @FROM_DATE = format(@FROM_DATE,'yyyy-MM-01')
      

      【讨论】:

        猜你喜欢
        • 2023-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-28
        • 2020-12-20
        • 1970-01-01
        • 2018-06-06
        • 1970-01-01
        相关资源
        最近更新 更多