【问题标题】:TSQL DATEADD using ticksTSQL DATEADD 使用刻度
【发布时间】:2013-06-21 17:03:02
【问题描述】:

上下文

在表值函数中,返回的“表”从 SELECT 填充到此表:

CREATE TABLE [org].[work_schedule_time](
   [id] [int] IDENTITY(1,1) NOT NULL,
   [fk_schedule] [int] NOT NULL,
   [date_start] [datetime] NOT NULL,
   [date_end] [datetime] NOT NULL,
   [is_repeating] [bit] NOT NULL,
   [repeat_interval] [bigint] NULL,
   [repeat_count] [int] NOT NULL,
   [is_available] [bit] NOT NULL,
 CONSTRAINT [PK_work_schedule_time] PRIMARY KEY CLUSTERED 
(
   [id] ASC
)

函数接受三个参数:

  1. @fkSchedule
  2. @dateMin
  3. @dateMax 用于日期边界

并返回一个包含正确填充日期范围的表格。例如,对于这一行

id  fk_schedule  date_start               date_end                 is_repeating  repeat_interval  repeat_count  is_available
3   1            2013-06-03 08:00:00.000  2013-06-03 17:00:00.000  1             6048000000000    0             1

这样调用函数

SELECT * FROM [org].[work_schedule_time_fn](1, '2013-06-01', '2013-07-01')

应该返回一个类似的表

schedule_id  date_start               date_end
1            2013-06-03 08:00:00.000  2013-06-03 17:00:00.000
1            2013-06-10 08:00:00.000  2013-06-10 17:00:00.000
1            2013-06-17 08:00:00.000  2013-06-17 17:00:00.000
1            2013-06-24 08:00:00.000  2013-06-24 17:00:00.000

问题

repeat_interval 列是 bigint,代表 C# TimeSpan。 (表中已有数据,应用逻辑已在使用中。)

我需要调用DATEADD(ms, @repeatInterval / 1000, @dateStart),但我得到了Arithmetic overflow error converting expression to data type int

我已经阅读了几篇关于它的帖子,但一些答案建议减去 552877920000000000 或其他一些魔术数字,或其他一些巫术技巧。

那么,如果我有一个包含一些刻度的 bigint 值,我需要将其添加到 datetime 列,那么最正确的方法是什么?

谢谢。

最终解决方案

为了避免在滴答数过多时再次溢出,我最终添加了分钟,然后是秒,然后是毫秒。

SET @dateStart = DATEADD(ms, (@repeatInterval % 10000000) / 10000, DATEADD(s, CAST(@repeatInterval / 10000000 as int) % 60, DATEADD(n, CAST(@repeatInterval / 600000000 as int), @dateStart)));

【问题讨论】:

    标签: c# sql-server tsql datetime sql-function


    【解决方案1】:

    我认为问题在于dateadd() 函数采用int 参数,除法的结果对于 int 来说太大了。

    你可以试试这个:

    select dateadd(ms, (@repeatInterval%10000000)/10000 , DATEADD(s, cast(@repeatInterval / 10000000 as int), @dateStart))
    

    即先加“秒”,再加毫秒。

    如果秒数仍然太大,您可以移动到越来越大的时间范围。

    【讨论】:

    • 太棒了!谢谢。我想我已经在某个论坛上阅读过这个解决方案,但不知何故它确实影响了我。 (请注意,我更正了值以转换每秒刻度。)
    【解决方案2】:

    问题在于@repeatInterval / 1000 我相信这个结果将是浮动的(来自记忆)

    我使用它来工作,将除法的结果转换回 bigint。您可能需要先对其进行四舍五入,因为普通转换的结果可能会导致不准确。

    declare @dateStart dateTime
    declare @repeatInterval bigint
    
    set @dateStart = GetDate()
    set @repeatInterval = 30000
    
    select @dateStart, DATEADD(millisecond, cast((@repeatInterval / 1000) as bigint), @dateStart)
    

    【讨论】:

      【解决方案3】:

      选择的答案虽然在大多数情况下完全有效且可用,但会失去潜在的精度和规模。即使刻度精确到 100ns,刻度也会被截断到毫秒,并且当出现超过 68 年的时间跨度时,表达式仍然会溢出。两者都可以通过从毫秒切换到纳秒并在几天内添加第三个 dateadd 级别来避免。我知道这是矫枉过正,但以下表达式几乎适用于任何可以用 sql server 数据类型表示的持续时间。

      Declare @sec bigint = 10000000;
      Declare
          -- Our starting point in time.
          @Moment datetimeoffset = N'1000-01-01 00:00:00.0000000+02:00'
      
          -- Several durations stored as ticks
      ,   @TicksSmall bigint = 1234567
      ,   @TicksMedium bigint =
              (12 * @sec * 60) + (34 * @sec)
          +   1234567
      ,   @TicksBig bigint =
              (DateDiff( Day, N'1000-01-01', N'1000-12-12' ) * @sec * 60 * 60 * 24)
          +   (12 * @sec * 60 * 60) + (34 * @sec * 60) + (56 * @sec)
          +   1234567
      ,   @TicksHuge bigint =
              (DateDiff( Day, N'1000-01-01', N'9999-12-12' ) * @sec * 60 * 60 * 24)
          +   (12 * @sec * 60 * 60) + (34 * @sec * 60) + (56 * @sec)
          +   1234567
      ;
      
      Select
          @TicksSmall As Ticks
      ,   DateAdd( nanosecond, Convert( int, (@TicksSmall % 10000000) * 100 )
          ,   DateAdd( second, Convert( int, (@TicksSmall % 864000000000) / 10000000 )
              ,   DateAdd( day, Convert( int, @TicksSmall / 864000000000 ), @Moment )
              )
          ) As Moment
      Union all
      Select
          @TicksMedium
      ,   DateAdd( nanosecond, Convert( int, (@TicksMedium % 10000000) * 100 )
          ,   DateAdd( second, Convert( int, (@TicksMedium % 864000000000) / 10000000 )
              ,   DateAdd( day, Convert( int, @TicksMedium / 864000000000 ), @Moment )
              )
          )
      Union all
      Select
          @TicksBig
      ,   DateAdd( nanosecond, Convert( int, (@TicksBig % 10000000) * 100 )
          ,   DateAdd( second, Convert( int, (@TicksBig % 864000000000) / 10000000 )
              ,   DateAdd( day, Convert( int, @TicksBig / 864000000000 ), @Moment )
              )
          )
      Union all
      Select
          @TicksHuge
      ,   DateAdd( nanosecond, Convert( int, (@TicksHuge % 10000000) * 100 )
          ,   DateAdd( second, Convert( int, (@TicksHuge % 864000000000) / 10000000 )
              ,   DateAdd( day, Convert( int, @TicksHuge / 864000000000 ), @Moment )
              )
          )
      ;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-01-09
        • 2013-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-11
        相关资源
        最近更新 更多