存储值> 24:00:00的.Net Timespan的正确SQL类型是什么?

Gra*_*ler 185 .net sql-server timespan

我试图TimeSpan在SQL Server 2008 R2中存储.Net .

EF Code First似乎建议它应该存储为Time(7)SQL中的一个.

但是TimeSpan.Net可以处理比24小时更长的时间.

TimeSpan在SQL服务器中处理存储.Net的最佳方法是什么?

Tom*_*ler 211

我将它作为a存储在数据库中,BIGINT并存储刻度数(例如TimeSpan.Ticks属性).

这样,如果我想在检索时获取TimeSpan对象,我可以做一些简单的TimeSpan.FromTicks(value).

  • 我可能会将刻度转换成这样的时间对象:`SELECT CAST(DATEADD(MILLISECOND,@ Ticks/CAST(10000 AS BIGINT),'1900-01-01')AS TIME)`.`'1900-01-01'日期并不重要,当然,它只是`DATEADD(...)`函数所需的第三个变量.请记住,刻度中有100纳秒,但是如果你使用`DATEADD(NANOSECOND ......)你可能会溢出,因此使用毫秒.还要记住你应该使用C#`TimeSpan.TicksPerMillisecond`来检查这个事实(应该是万)确定. (9认同)
  • 您将如何处理sql中的计算,让您说您需要计算它包含多少小时? (2认同)

Gra*_*ler 62

感谢您的建议.因为SQL服务器中没有等效的.我只是创建了一个第二个字段,它将TimeSpan转换为刻度并将其存储在数据库中.然后我阻止了存储TimeSpan

public Int64 ValidityPeriodTicks { get; set; }

[NotMapped]
public TimeSpan ValidityPeriod
{
    get { return TimeSpan.FromTicks(ValidityPeriodTicks); }
    set { ValidityPeriodTicks = value.Ticks; }
}
Run Code Online (Sandbox Code Playgroud)

  • 对于使用EF Core的任何人 - 在2.1中你可以使用价值转换和TimeSpanToTicksConverter透明地将时间跨度映射到数据库中的滴答 (4认同)

Ale*_*Río 31

如果您不必存储超过24小时,则可以只存储时间,因为SQL Server 2008及更高版本的映射是

time (SQL Server) <-> TimeSpan(.NET)

如果您只需要24小时或更短时间存储,则无需转换.

来源:http://msdn.microsoft.com/en-us/library/cc716729(v = vs1010).aspx

但是,如果要存储超过24小时,则需要将其存储在刻度线中,检索数据然后转换为TimeSpan.例如

int timeData = yourContext.yourTable.FirstOrDefault();
TimeSpan ts = TimeSpan.FromMilliseconds(timeData);
Run Code Online (Sandbox Code Playgroud)

  • 正如OP所说,SQL Server中的"时间"DataType仅支持24小时,他希望存储> 24小时 (22认同)
  • 此外,TimeSpan(.NET)可能是负数,而Time(SQL Server)则不能. (11认同)
  • 时间和持续时间之间存在重大差异.时间表示某一天的时间,而持续时间是两个时刻之间的差异.将其与位置(时间)和距离(持续时间)进行比较. (11认同)
  • ^完全正确. - SQL`Time`类型不是表示持续时间,而是表示DateTime值的Time部分; 对于"TimeSpan"来说,这是一个糟糕的选择. (3认同)

fea*_*net 19

没有直接的等价物.只需以数字方式存储,例如秒数或适合您所需精度的东西.


Mov*_*GP0 8

There are multiple ways how to present a timespan in the database.

time

This datatype is supported since SQL Server 2008 and is the prefered way to store a TimeSpan. There is no mapping needed. It also works well with SQL code.

public TimeSpan ValidityPeriod { get; set; }
Run Code Online (Sandbox Code Playgroud)

However, as stated in the original question, this datatype is limited to 24 hours.

datetimeoffset

The datetimeoffset datatype maps directly to System.DateTimeOffset. It's used to express the offset between a datetime/datetime2 to UTC, but you can also use it for TimeSpan.

However, since the datatype suggests a very specific semantic, so you should also consider other options.

datetime / datetime2

One approach might be to use the datetime or datetime2 types. This is best in scenarios where you need to process the values in the database directly, ie. for views, stored procedures, or reports. The drawback is that you need to substract the value DateTime(1900,01,01,00,00,00) from the date to get back the timespan in your business logic.

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; }
}
Run Code Online (Sandbox Code Playgroud)

bigint

Another approach might be to convert the TimeSpan into ticks and use the bigint datatype. However, this approach has the drawback that it's cumbersome to use in SQL queries.

public long ValidityPeriod { get; set; }

[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
    get { return TimeSpan.FromTicks(ValidityPeriod); }
    set { ValidityPeriod = value.Ticks; }
}
Run Code Online (Sandbox Code Playgroud)

varchar(N)

This is best for cases where the value should be readable by humans. You might also use this format in SQL queries by utilizing the CONVERT(datetime, ValidityPeriod) function. Dependent on the required precision, you will need between 8 and 25 characters.

public string ValidityPeriod { get; set; }

[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
    get { return TimeSpan.Parse(ValidityPeriod); }
    set { ValidityPeriod = value.ToString("HH:mm:ss"); }
}
Run Code Online (Sandbox Code Playgroud)

Bonus: Period and Duration

Using a string, you can also store NodaTime datatypes, especially Duration and Period. The first is basically the same as a TimeSpan, while the later respects that some days and months are longer or shorter than others (ie. January has 31 days and February has 28 or 29; some days are longer or shorter because of daylight saving time). In such cases, using a TimeSpan is the wrong choice.

You can use this code to convert Periods:

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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

And then use it like

public string ValidityPeriod { get; set; }

[NotMapped]
public Period ValidityPeriodPeriod
{
    get => ValidityPeriod.ToPeriod();
    set => ValidityPeriod = value.ToString();
}
Run Code Online (Sandbox Code Playgroud)

I really like NodaTime and it often saves me from tricky bugs and lots of headache. The drawback here is that you really can't use it in SQL queries and need to do calculations in-memory.

CLR User-Defined Type

You also have the option to use a custom datatype and support a custom TimeSpan class directly. See CLR User-Defined Types for details.

The drawback here is that the datatype might not behave well with SQL Reports. Also, some versions of SQL Server (Azure, Linux, Data Warehouse) are not supported.

Value Conversions

Starting with EntityFramework Core 2.1, you have the option to use Value Conversions.

However, when using this, EF will not be able to convert many queries into SQL, causing queries to run in-memory; potentially transfering lots and lots of data to your application.

So at least for now, it might be better not to use it, and just map the query result with Automapper.


ric*_*ick 7

我知道这是一个老问题,但我想确保注意到其他几个选项.

由于您无法在sql数据类型字段中存储超过24小时的TimeSpan; 可能还有其他两种选择.

  1. 使用varchar(xx)存储TimeSpan的ToString.这样做的好处是不必将精度烘焙到数据类型或计算中(秒与毫秒相比,天与星期四)所有你需要的是使用TimeSpan.Parse/TryParse.这就是我要做的.

  2. 使用第二个日期,日期时间或日期时间偏移,存储第一个日期+时间跨度的结果.从db读取是TimeSpan x = SecondDate - FirstDate的问题.使用此选项将保护您,以便其他非.NET数据访问库访问相同的数据,但不了解TimeSpans; 如果你有这样的环境.


Win*_*jam 7

现在,使用 EF Core,您可以在您的应用程序中透明地转换数据类型AppDbContext

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      // i.e. Store TimeSpan as string (custom)
      modelBuilder
        .Entity<YourClass>()
        .Property(x => x.YourTimeSpan)
        .HasConversion(
            timeSpan => timeSpan.ToString(), // To DB
            timeSpanString => TimeSpan.Parse(timeSpanString) // From DB
        );

    // i.e. Store TimeSpan as string (using TimeSpanToStringConverter)
    modelBuilder
        .Entity<YourClass>()
        .Property(x => x.YourTimeSpan)
        .HasConversion(new TimeSpanToStringConverter());

      // i.e. Store TimeSpan as number of ticks (custom)
      modelBuilder
        .Entity<YourClass>()
        .Property(x => x.YourTimeSpan)
        .HasConversion(
            timeSpan => timeSpan.Ticks, // To DB
            timeSpanString => TimeSpan.FromTicks(timeSpanString) // From DB
        );

    // i.e. Store TimeSpan as number of ticks (using TimeSpanToTicksConverter)
    modelBuilder
        .Entity<YourClass>()
        .Property(x => x.YourTimeSpan)
        .HasConversion(new TimeSpanToTicksConverter());
}
Run Code Online (Sandbox Code Playgroud)