什么是正确的SQLtypes来存储.Net Timespan值> 24:00:00?
我正在尝试在SQL Server 2008 R2中存储.Net TimeSpan
。
EF Code First似乎认为它应该作为SQL Time(7)
存储。
但.Net中的TimeSpan
可以处理比24小时更长的时间。
SQL Server中处理存储.Net TimeSpan
的最佳方式是什么?
我将它作为一个BIGINT
存储在数据库中,我会存储滴答数(例如TimeSpan.Ticks属性)。
这样,如果我想获取TimeSpan对象时,我可以做TimeSpan.FromTicks(value) ,这很容易。
感谢您的build议。 因为在SQL服务器中没有等价物。 我只是创build了第二个字段,将TimeSpan转换为刻度并将其存储在数据库中。 然后我阻止存储TimeSpan
public Int64 ValidityPeriodTicks { get; set; } [NotMapped] public TimeSpan ValidityPeriod { get { return TimeSpan.FromTicks(ValidityPeriodTicks); } set { ValidityPeriodTicks = value.Ticks; } }
如果您不必存储超过24小时的时间 ,则可以只存储时间 ,因为SQL Server 2008及更高版本是映射
time (SQL Server) <-> TimeSpan(.NET)
如果您只需要存储24小时或更less,则不需要转换。
资料来源: http : //msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
但是 ,如果要存储超过24小时,则需要将其存储在蜱中,检索数据,然后转换为TimeSpan。 例如
int timeData = yourContext.yourTable.FirstOrDefault(); TimeSpan ts = TimeSpan.FromMilliseconds(timeData);
没有直接的等价物。 只需将其存储在数字中,例如秒数或适合您要求的准确度的值。
我知道这是一个古老的问题,但我想确保其他一些选项的logging。
由于您无法在时间sql数据types字段中存储大于24小时的TimeSpan, 还有其他一些select可能。
-
使用varchar(xx)存储TimeSpan的ToString。 这样做的好处是精度不必被烘焙到数据types或计算中,(秒vs毫秒vs天vs四周)所有你需要使用的是TimeSpan.Parse / TryParse。 这是我会做的。
-
使用第二个datedatetime或datetimeoffset,它存储第一个date+时间范围的结果。 从数据库中读取是TimeSpan x = SecondDate – FirstDate的问题。 使用这个选项将保护你的其他非.NET数据访问库访问相同的数据,但不理解TimeSpans; 如果你有这样的环境。
为了与生成时间跨度(计算2次或date时间的差异)最可能的来源相一致,您可能需要将.NET TimeSpan
存储为SQL Server DateTime
types。
这是因为在SQL Server中,两个DateTime
的差异( Cast
to Float
和Cast
to DateTime
)仅仅是一个相对于1900年1月1日的DateTime
。 相差+0.1秒将是1900年1月1日00:00:00.100和-0.1秒将是1899年12月31日23:59:59.900。
要将.NET TimeSpan
转换为SQL Server DateTime
Type,首先要将其转换为.NET DateTime
Type,并将其添加到1900年1月1日的DateTime
中。当然,当您从SQL Server将它读入.NET时,首先将它读入.NET DateTime
,然后从中减去1900年1月1日将其转换为.NET TimeSpan
。
对于从SQL Server DateTime
和SQL Server(即通过T-SQL)生成时间跨度并且SQL Server在2016之前生成时间跨度的用例,根据您的范围和精度需求,可能不太实际他们为毫秒(更不用说Ticks
),因为由DateDiff
返回的Int
types(与来自SS 2016 +的DateDiff_Big
的BigInt
)在DateDiff_Big
天的毫秒和〜67年之后溢出。 秒。 鉴于此解决scheme将精确处理时间跨度为0.1秒,从-147到+ 8099年。
警告:
-
这只有在相对于1900年1月1日的差别会导致在SQL Server
DateTime
Type(1753年1月1日到9999年12月31日,也就是-147年到+ 8099年)范围内的值的情况下才有效。 我们不必担心.NETTimeSpan
方面的问题,因为它可以保持在29 K到29 K的范围内。 我没有提到SQL ServerDateTime2
Type(其范围在负面上比SQL ServerDateTime
大得多),因为:a)它不能通过简单的Cast
转换为数字b)DateTime
'对于绝大多数的用例来说,这个范围就足够了。 -
通过
Cast
到Float
– and – back方法计算的SQL ServerDateTime
差异在0.1秒Float
似乎不准确。
通常情况下,我将TimeSpan存储为一个bigint,并填入TimeSpan.Ticks属性中的tick,如前所述。 您还可以将TimeSpan存储为使用TimeSpan.ToString()的输出填充的varchar(26)。 我写的四个标量函数(ConvertFromTimeSpanString,ConvertToTimeSpanString,DateAddTicks,DateDiffTicks)对于处理SQL端的TimeSpan很有帮助,并且避免了会产生人为限制范围的黑客行为。 如果你可以把时间间隔存储在一个.NET TimeSpan中,它也应该使用这些函数。 此外,这些function还允许您使用TimeSpans和100纳秒滴答,即使在使用不包含.NET Framework的技术时也是如此。
DROP FUNCTION [dbo].[DateDiffTicks] GO DROP FUNCTION [dbo].[DateAddTicks] GO DROP FUNCTION [dbo].[ConvertToTimeSpanString] GO DROP FUNCTION [dbo].[ConvertFromTimeSpanString] GO SET ANSI_NULLS OFF GO SET QUOTED_IDENTIFIER OFF GO -- ============================================= -- Author: James Coe -- Create date: 2011-05-23 -- Description: Converts from a varchar(26) TimeSpan string to a bigint containing the number of 100 nanosecond ticks. -- ============================================= /* [-][d.]hh:mm:ss[.fffffff] "-" A minus sign, which indicates a negative time interval. No sign is included for a positive time span. "d" The number of days in the time interval. This element is omitted if the time interval is less than one day. "hh" The number of hours in the time interval, ranging from 0 to 23. "mm" The number of minutes in the time interval, ranging from 0 to 59. "ss" The number of seconds in the time interval, ranging from 0 to 59. "fffffff" Fractional seconds in the time interval. This element is omitted if the time interval does not include fractional seconds. If present, fractional seconds are always expressed using seven decimal digits. */ CREATE FUNCTION [dbo].[ConvertFromTimeSpanString] (@timeSpan varchar(26)) RETURNS bigint AS BEGIN DECLARE @hourStart int DECLARE @minuteStart int DECLARE @secondStart int DECLARE @ticks bigint DECLARE @hours bigint DECLARE @minutes bigint DECLARE @seconds DECIMAL(9, 7) SET @hourStart = CHARINDEX('.', @timeSpan) + 1 SET @minuteStart = CHARINDEX(':', @timeSpan) + 1 SET @secondStart = CHARINDEX(':', @timespan, @minuteStart) + 1 SET @ticks = 0 IF (@hourStart > 1 AND @hourStart < @minuteStart) BEGIN SET @ticks = CONVERT(bigint, LEFT(@timespan, @hourstart - 2)) * 864000000000 END ELSE BEGIN SET @hourStart = 1 END SET @hours = CONVERT(bigint, SUBSTRING(@timespan, @hourStart, @minuteStart - @hourStart - 1)) SET @minutes = CONVERT(bigint, SUBSTRING(@timespan, @minuteStart, @secondStart - @minuteStart - 1)) SET @seconds = CONVERT(DECIMAL(9, 7), SUBSTRING(@timespan, @secondStart, LEN(@timeSpan) - @secondStart + 1)) IF (@ticks < 0) BEGIN SET @ticks = @ticks - @hours * 36000000000 END ELSE BEGIN SET @ticks = @ticks + @hours * 36000000000 END IF (@ticks < 0) BEGIN SET @ticks = @ticks - @minutes * 600000000 END ELSE BEGIN SET @ticks = @ticks + @minutes * 600000000 END IF (@ticks < 0) BEGIN SET @ticks = @ticks - @seconds * 10000000.0 END ELSE BEGIN SET @ticks = @ticks + @seconds * 10000000.0 END RETURN @ticks END GO -- ============================================= -- Author: James Coe -- Create date: 2011-05-23 -- Description: Converts from a bigint containing the number of 100 nanosecond ticks to a varchar(26) TimeSpan string. -- ============================================= /* [-][d.]hh:mm:ss[.fffffff] "-" A minus sign, which indicates a negative time interval. No sign is included for a positive time span. "d" The number of days in the time interval. This element is omitted if the time interval is less than one day. "hh" The number of hours in the time interval, ranging from 0 to 23. "mm" The number of minutes in the time interval, ranging from 0 to 59. "ss" The number of seconds in the time interval, ranging from 0 to 59. "fffffff" Fractional seconds in the time interval. This element is omitted if the time interval does not include fractional seconds. If present, fractional seconds are always expressed using seven decimal digits. */ CREATE FUNCTION [dbo].[ConvertToTimeSpanString] (@ticks bigint) RETURNS varchar(26) AS BEGIN DECLARE @timeSpanString varchar(26) IF (@ticks < 0) BEGIN SET @timeSpanString = '-' END ELSE BEGIN SET @timeSpanString = '' END -- Days DECLARE @days bigint SET @days = FLOOR(ABS(@ticks / 864000000000.0)) IF (@days > 0) BEGIN SET @timeSpanString = @timeSpanString + CONVERT(varchar(26), @days) + '.' END SET @ticks = ABS(@ticks % 864000000000) -- Hours SET @timeSpanString = @timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(@ticks / 36000000000.0)), 2) + ':' SET @ticks = @ticks % 36000000000 -- Minutes SET @timeSpanString = @timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(@ticks / 600000000.0)), 2) + ':' SET @ticks = @ticks % 600000000 -- Seconds SET @timeSpanString = @timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(@ticks / 10000000.0)), 2) SET @ticks = @ticks % 10000000 -- Fractional Seconds IF (@ticks > 0) BEGIN SET @timeSpanString = @timeSpanString + '.' + LEFT(CONVERT(varchar(26), @ticks) + '0000000', 7) END RETURN @timeSpanString END GO -- ============================================= -- Author: James Coe -- Create date: 2011-05-23 -- Description: Adds the specified number of 100 nanosecond ticks to a date. -- ============================================= CREATE FUNCTION [dbo].[DateAddTicks] ( @ticks bigint , @starting_date datetimeoffset ) RETURNS datetimeoffset AS BEGIN DECLARE @dateTimeResult datetimeoffset IF (@ticks < 0) BEGIN -- Hours SET @dateTimeResult = DATEADD(HOUR, CEILING(@ticks / 36000000000.0), @starting_date) SET @ticks = @ticks % 36000000000 -- Seconds SET @dateTimeResult = DATEADD(SECOND, CEILING(@ticks / 10000000.0), @dateTimeResult) SET @ticks = @ticks % 10000000 -- Nanoseconds SET @dateTimeResult = DATEADD(NANOSECOND, @ticks * 100, @dateTimeResult) END ELSE BEGIN -- Hours SET @dateTimeResult = DATEADD(HOUR, FLOOR(@ticks / 36000000000.0), @starting_date) SET @ticks = @ticks % 36000000000 -- Seconds SET @dateTimeResult = DATEADD(SECOND, FLOOR(@ticks / 10000000.0), @dateTimeResult) SET @ticks = @ticks % 10000000 -- Nanoseconds SET @dateTimeResult = DATEADD(NANOSECOND, @ticks * 100, @dateTimeResult) END RETURN @dateTimeResult END GO -- ============================================= -- Author: James Coe -- Create date: 2011-05-23 -- Description: Gets the difference between two dates in 100 nanosecond ticks. -- ============================================= CREATE FUNCTION [dbo].[DateDiffTicks] ( @starting_date datetimeoffset , @ending_date datetimeoffset ) RETURNS bigint AS BEGIN DECLARE @ticks bigint DECLARE @days bigint DECLARE @hours bigint DECLARE @minutes bigint DECLARE @seconds bigint SET @hours = DATEDIFF(HOUR, @starting_date, @ending_date) SET @starting_date = DATEADD(HOUR, @hours, @starting_date) SET @ticks = @hours * 36000000000 SET @seconds = DATEDIFF(SECOND, @starting_date, @ending_date) SET @starting_date = DATEADD(SECOND, @seconds, @starting_date) SET @ticks = @ticks + @seconds * 10000000 SET @ticks = @ticks + CONVERT(bigint, DATEDIFF(NANOSECOND, @starting_date, @ending_date)) / 100 RETURN @ticks END GO --- BEGIN Test Harness --- SET NOCOUNT ON DECLARE @dateTimeOffsetMinValue datetimeoffset DECLARE @dateTimeOffsetMaxValue datetimeoffset DECLARE @timeSpanMinValueString varchar(26) DECLARE @timeSpanZeroString varchar(26) DECLARE @timeSpanMaxValueString varchar(26) DECLARE @timeSpanMinValueTicks bigint DECLARE @timeSpanZeroTicks bigint DECLARE @timeSpanMaxValueTicks bigint DECLARE @dateTimeOffsetMinMaxDiffTicks bigint DECLARE @dateTimeOffsetMaxMinDiffTicks bigint SET @dateTimeOffsetMinValue = '0001-01-01T00:00:00.0000000+00:00' SET @dateTimeOffsetMaxValue = '9999-12-31T23:59:59.9999999+00:00' SET @timeSpanMinValueString = '-10675199.02:48:05.4775808' SET @timeSpanZeroString = '00:00:00' SET @timeSpanMaxValueString = '10675199.02:48:05.4775807' SET @timeSpanMinValueTicks = -9223372036854775808 SET @timeSpanZeroTicks = 0 SET @timeSpanMaxValueTicks = 9223372036854775807 SET @dateTimeOffsetMinMaxDiffTicks = 3155378975999999999 SET @dateTimeOffsetMaxMinDiffTicks = -3155378975999999999 -- TimeSpan Conversion Tests PRINT 'Testing TimeSpan conversions...' DECLARE @convertToTimeSpanStringMinTicksResult varchar(26) DECLARE @convertFromTimeSpanStringMinTimeSpanResult bigint DECLARE @convertToTimeSpanStringZeroTicksResult varchar(26) DECLARE @convertFromTimeSpanStringZeroTimeSpanResult bigint DECLARE @convertToTimeSpanStringMaxTicksResult varchar(26) DECLARE @convertFromTimeSpanStringMaxTimeSpanResult bigint SET @convertToTimeSpanStringMinTicksResult = dbo.ConvertToTimeSpanString(@timeSpanMinValueTicks) SET @convertFromTimeSpanStringMinTimeSpanResult = dbo.ConvertFromTimeSpanString(@timeSpanMinValueString) SET @convertToTimeSpanStringZeroTicksResult = dbo.ConvertToTimeSpanString(@timeSpanZeroTicks) SET @convertFromTimeSpanStringZeroTimeSpanResult = dbo.ConvertFromTimeSpanString(@timeSpanZeroString) SET @convertToTimeSpanStringMaxTicksResult = dbo.ConvertToTimeSpanString(@timeSpanMaxValueTicks) SET @convertFromTimeSpanStringMaxTimeSpanResult = dbo.ConvertFromTimeSpanString(@timeSpanMaxValueString) -- Test Results SELECT 'Convert to TimeSpan String from Ticks (Minimum)' AS Test , CASE WHEN @convertToTimeSpanStringMinTicksResult = @timeSpanMinValueString THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @timeSpanMinValueTicks AS [Ticks] , CONVERT(varchar(26), NULL) AS [TimeSpan String] , CONVERT(varchar(26), @convertToTimeSpanStringMinTicksResult) AS [Actual Result] , CONVERT(varchar(26), @timeSpanMinValueString) AS [Expected Result] UNION ALL SELECT 'Convert from TimeSpan String to Ticks (Minimum)' AS Test , CASE WHEN @convertFromTimeSpanStringMinTimeSpanResult = @timeSpanMinValueTicks THEN 'Pass' ELSE 'Fail' END AS [Test Status] , NULL AS [Ticks] , @timeSpanMinValueString AS [TimeSpan String] , CONVERT(varchar(26), @convertFromTimeSpanStringMinTimeSpanResult) AS [Actual Result] , CONVERT(varchar(26), @timeSpanMinValueTicks) AS [Expected Result] UNION ALL SELECT 'Convert to TimeSpan String from Ticks (Zero)' AS Test , CASE WHEN @convertToTimeSpanStringZeroTicksResult = @timeSpanZeroString THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @timeSpanZeroTicks AS [Ticks] , CONVERT(varchar(26), NULL) AS [TimeSpan String] , CONVERT(varchar(26), @convertToTimeSpanStringZeroTicksResult) AS [Actual Result] , CONVERT(varchar(26), @timeSpanZeroString) AS [Expected Result] UNION ALL SELECT 'Convert from TimeSpan String to Ticks (Zero)' AS Test , CASE WHEN @convertFromTimeSpanStringZeroTimeSpanResult = @timeSpanZeroTicks THEN 'Pass' ELSE 'Fail' END AS [Test Status] , NULL AS [Ticks] , @timeSpanZeroString AS [TimeSpan String] , CONVERT(varchar(26), @convertFromTimeSpanStringZeroTimeSpanResult) AS [Actual Result] , CONVERT(varchar(26), @timeSpanZeroTicks) AS [Expected Result] UNION ALL SELECT 'Convert to TimeSpan String from Ticks (Maximum)' AS Test , CASE WHEN @convertToTimeSpanStringMaxTicksResult = @timeSpanMaxValueString THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @timeSpanMaxValueTicks AS [Ticks] , CONVERT(varchar(26), NULL) AS [TimeSpan String] , CONVERT(varchar(26), @convertToTimeSpanStringMaxTicksResult) AS [Actual Result] , CONVERT(varchar(26), @timeSpanMaxValueString) AS [Expected Result] UNION ALL SELECT 'Convert from TimeSpan String to Ticks (Maximum)' AS Test , CASE WHEN @convertFromTimeSpanStringMaxTimeSpanResult = @timeSpanMaxValueTicks THEN 'Pass' ELSE 'Fail' END AS [Test Status] , NULL AS [Ticks] , @timeSpanMaxValueString AS [TimeSpan String] , CONVERT(varchar(26), @convertFromTimeSpanStringMaxTimeSpanResult) AS [Actual Result] , CONVERT(varchar(26), @timeSpanMaxValueTicks) AS [Expected Result] -- Ticks Date Add Test PRINT 'Testing DateAddTicks...' DECLARE @DateAddTicksPositiveTicksResult datetimeoffset DECLARE @DateAddTicksZeroTicksResult datetimeoffset DECLARE @DateAddTicksNegativeTicksResult datetimeoffset SET @DateAddTicksPositiveTicksResult = dbo.DateAddTicks(@dateTimeOffsetMinMaxDiffTicks, @dateTimeOffsetMinValue) SET @DateAddTicksZeroTicksResult = dbo.DateAddTicks(@timeSpanZeroTicks, @dateTimeOffsetMinValue) SET @DateAddTicksNegativeTicksResult = dbo.DateAddTicks(@dateTimeOffsetMaxMinDiffTicks, @dateTimeOffsetMaxValue) -- Test Results SELECT 'Date Add with Ticks Test (Positive)' AS Test , CASE WHEN @DateAddTicksPositiveTicksResult = @dateTimeOffsetMaxValue THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @dateTimeOffsetMinMaxDiffTicks AS [Ticks] , @dateTimeOffsetMinValue AS [Starting Date] , @DateAddTicksPositiveTicksResult AS [Actual Result] , @dateTimeOffsetMaxValue AS [Expected Result] UNION ALL SELECT 'Date Add with Ticks Test (Zero)' AS Test , CASE WHEN @DateAddTicksZeroTicksResult = @dateTimeOffsetMinValue THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @timeSpanZeroTicks AS [Ticks] , @dateTimeOffsetMinValue AS [Starting Date] , @DateAddTicksZeroTicksResult AS [Actual Result] , @dateTimeOffsetMinValue AS [Expected Result] UNION ALL SELECT 'Date Add with Ticks Test (Negative)' AS Test , CASE WHEN @DateAddTicksNegativeTicksResult = @dateTimeOffsetMinValue THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @dateTimeOffsetMaxMinDiffTicks AS [Ticks] , @dateTimeOffsetMaxValue AS [Starting Date] , @DateAddTicksNegativeTicksResult AS [Actual Result] , @dateTimeOffsetMinValue AS [Expected Result] -- Ticks Date Diff Test PRINT 'Testing Date Diff Ticks...' DECLARE @dateDiffTicksMinMaxResult bigint DECLARE @dateDiffTicksMaxMinResult bigint SET @dateDiffTicksMinMaxResult = dbo.DateDiffTicks(@dateTimeOffsetMinValue, @dateTimeOffsetMaxValue) SET @dateDiffTicksMaxMinResult = dbo.DateDiffTicks(@dateTimeOffsetMaxValue, @dateTimeOffsetMinValue) -- Test Results SELECT 'Date Difference in Ticks Test (Min, Max)' AS Test , CASE WHEN @dateDiffTicksMinMaxResult = @dateTimeOffsetMinMaxDiffTicks THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @dateTimeOffsetMinValue AS [Starting Date] , @dateTimeOffsetMaxValue AS [Ending Date] , @dateDiffTicksMinMaxResult AS [Actual Result] , @dateTimeOffsetMinMaxDiffTicks AS [Expected Result] UNION ALL SELECT 'Date Difference in Ticks Test (Max, Min)' AS Test , CASE WHEN @dateDiffTicksMaxMinResult = @dateTimeOffsetMaxMinDiffTicks THEN 'Pass' ELSE 'Fail' END AS [Test Status] , @dateTimeOffsetMaxValue AS [Starting Date] , @dateTimeOffsetMinValue AS [Ending Date] , @dateDiffTicksMaxMinResult AS [Actual Result] , @dateTimeOffsetMaxMinDiffTicks AS [Expected Result] PRINT 'Tests Complete.' GO --- END Test Harness ---