我试图在SQL server 2008 R2中存储. net TimeSpan。
EF Code First似乎建议它应该存储为SQL中的时间(7)。
然而。net中的TimeSpan可以处理超过24小时的时间。
在SQL server中存储。net TimeSpan的最佳方法是什么?
我试图在SQL server 2008 R2中存储. net TimeSpan。
EF Code First似乎建议它应该存储为SQL中的时间(7)。
然而。net中的TimeSpan可以处理超过24小时的时间。
在SQL server中存储。net TimeSpan的最佳方法是什么?
当前回答
为了与最可能生成时间跨度(计算2倍或date-times的差异)的来源保持一致,您可能希望将. net TimeSpan存储为SQL Server DateTime类型。
这是因为在SQL Server中,2个DateTime的差值(转换为Float's,然后转换回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类型,首先要将其添加到1900年1月1日的DateTime中,从而将其转换为. net DateTime类型。当然,当你把它从SQL Server读入。net时,你会先把它读入。net DateTime,然后减去1900年1月1日,将它转换成。net TimeSpan。
For use cases where the time spans are being generated from SQL Server DateTime's and within SQL Server (i.e. via T-SQL) and SQL Server is prior to 2016, depending on your range and precision needs, it may not be practical to store them as milliseconds (not to mention Ticks) because the Int Type returned by DateDiff (vs. the BigInt from SS 2016+'s DateDiff_Big) overflows after ~24 days worth of milliseconds and ~67 yrs. of seconds. Whereas, this solution will handle time spans with precision down to 0.1 seconds and from -147 to +8,099 yrs..
警告:
This would only work if the difference relative to Jan. 1, 1900 would result in a value within the range of a SQL Server DateTime Type (Jan. 1, 1753 to Dec. 31, 9999 aka -147 to +8,099 yrs.). We don't have to worry near as much on the .NET TimeSpan side, since it can hold ~29 k to +29 k yrs. I didn't mention the SQL Server DateTime2 Type (whose range, on the negative side, is much greater than SQL Server DateTime's), because: a) it cannot be converted to a numeric via a simple Cast and b) DateTime's range should suffice for the vast majority of use cases. SQL Server DateTime differences computed via the Cast - to - Float - and - back method does not appear to be accurate beyond 0.1 seconds.
其他回答
没有直接的对等物。只需以数字形式存储,例如秒数或适合您所需精度的内容。
我将它作为BIGINT存储在数据库中,我将存储tick的数量(例如。时间间隔。蜱虫属性)。
这样,如果我想在检索时获得一个TimeSpan对象,我只需执行TimeSpan. fromticks (value),这很简单。
有多种方法可以在数据库中表示时间跨度。
time
从SQL Server 2008开始就支持这种数据类型,是存储TimeSpan的首选方式。不需要映射。它也可以很好地处理SQL代码。
public TimeSpan ValidityPeriod { get; set; }
然而,正如在最初的问题中所述,这种数据类型仅限于24小时。
日期时间偏移量
datetimeoffset数据类型直接映射到System.DateTimeOffset。它用于表示datetime/datetime2与UTC之间的偏移量,但您也可以将它用于TimeSpan。
但是,由于数据类型暗示了一种非常特定的语义,因此还应该考虑其他选项。
日期,时间
一种方法可能是使用datetime或datetime2类型。这在需要直接处理数据库中的值的场景中是最好的。用于视图、存储过程或报表。缺点是您需要从日期中减去DateTime(1900,01,01,00,00,00)值,以获得业务逻辑中的时间间隔。
public DateTime ValidityPeriod { get; set; }
[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
get { return ValidityPeriod - DateTime(1900,01,01,00,00,00); }
set { ValidityPeriod = DateTime(1900,01,01,00,00,00) + value; }
}
长整型数字
另一种方法可能是将TimeSpan转换为tick并使用bigint数据类型。但是,这种方法有一个缺点,在SQL查询中使用起来很麻烦。
public long ValidityPeriod { get; set; }
[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
get { return TimeSpan.FromTicks(ValidityPeriod); }
set { ValidityPeriod = value.Ticks; }
}
转换为(N)
这对于值应该由人类可读的情况是最好的。您还可以在SQL查询中使用CONVERT(datetime, ValidityPeriod)函数来使用这种格式。根据所需的精度,您将需要8到25个字符。
public string ValidityPeriod { get; set; }
[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
get { return TimeSpan.Parse(ValidityPeriod); }
set { ValidityPeriod = value.ToString("HH:mm:ss"); }
}
奖励:时间段和持续时间
使用字符串,还可以存储NodaTime数据类型,特别是Duration和Period。前者基本上与TimeSpan相同,而后者则考虑到某些日和月比其他日和月更长或更短。一月有31天,二月有28或29天;由于日光节约时间,有些日子变长或变短)。在这种情况下,使用TimeSpan是错误的选择。
你可以使用这段代码转换period:
using NodaTime;
using NodaTime.Serialization.JsonNet;
internal static class PeriodExtensions
{
public static Period ToPeriod(this string input)
{
var js = JsonSerializer.Create(new JsonSerializerSettings());
js.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
var quoted = string.Concat(@"""", input, @"""");
return js.Deserialize<Period>(new JsonTextReader(new StringReader(quoted)));
}
}
然后像这样使用它
public string ValidityPeriod { get; set; }
[NotMapped]
public Period ValidityPeriodPeriod
{
get => ValidityPeriod.ToPeriod();
set => ValidityPeriod = value.ToString();
}
我真的很喜欢NodaTime,它经常把我从棘手的bug和很多头痛中拯救出来。这里的缺点是不能在SQL查询中使用它,需要在内存中进行计算。
CLR自定义类型
您还可以选择直接使用自定义数据类型和支持自定义TimeSpan类。有关详细信息,请参见CLR用户定义类型。
这里的缺点是数据类型可能不能很好地使用SQL报表。此外,某些版本的SQL Server (Azure, Linux,数据仓库)不受支持。
值转换
从EntityFramework Core 2.1开始,你可以选择使用值转换。
然而,当使用这个时,EF将不能将许多查询转换为SQL,导致查询在内存中运行;潜在地向您的应用程序传输大量数据。
因此,至少现在,最好不要使用它,而只是用Automapper映射查询结果。
谢谢你的建议。因为在SQL server中没有对等的。我简单地创建了第二个字段,将TimeSpan转换为tick并将其存储在DB中。然后,我阻止存储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小时或更少。
来源:http://msdn.microsoft.com/en-us/library/cc716729 (v = vs.110) . aspx
但是,如果您希望存储超过24小时的时间,则需要以tick为单位存储它,检索数据,然后转换为TimeSpan。例如
int timeData = yourContext.yourTable.FirstOrDefault();
TimeSpan ts = TimeSpan.FromMilliseconds(timeData);