TSQL DATEADD使用ticks

时间:2013-06-25 02:29:16

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

上下文

在表值函数中,返回的"表"从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. a @dateMin
  3. 日期边界@dateMax
  4. 并返回一个包含正确填充日期范围的表格。例如,对于此行

    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_intervalbigint,代表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)));
    

3 个答案:

答案 0 :(得分:1)

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

你可以试试这个:

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

即,首先添加“秒”,然后添加毫秒。

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

答案 1 :(得分:0)

问题在于@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)

答案 2 :(得分:0)

选择的答案虽然在大多数情况下完全有效且可用,但却失去了潜在的精确度和规模。即使刻度精确到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 )
        )
    )
;