Estoy tratando de almacenar un .Net TimeSpan
en SQL Server 2008 R2.
EF Code First parece estar sugiriendo que debería almacenarse como a Time(7)
en SQL.
Sin embargo, TimeSpan
en .Net puede manejar períodos más largos de 24 horas.
¿Cuál es la mejor manera de manejar el almacenamiento de .Net TimeSpan
en el servidor SQL?
.net
sql-server
timespan
GraemeMiller
fuente
fuente
Respuestas:
Lo almacenaría en la base de datos como
BIGINT
ay almacenaría la cantidad de ticks (por ejemplo, TimeSpan.Ticks propiedad ).De esa manera, si quisiera obtener un objeto TimeSpan cuando lo recupere, podría hacer TimeSpan.FromTicks (valor), lo que sería fácil.
fuente
SELECT CAST(DATEADD(MILLISECOND, @Ticks/CAST(10000 AS BIGINT), '1900-01-01') AS TIME)
. La'1900-01-01'
fecha no importa, por supuesto, es solo la tercera variable requerida por laDATEADD(...)
función. Recuerde que hay 100 nanosegundos en una marca, pero si lo usaDATEADD(NANOSECOND...
, es probable que obtenga un desbordamiento, por lo tanto, use milisegundos. También recuerde que debe verificar este hecho usando C #TimeSpan.TicksPerMillisecond
(debe ser 10000) para estar seguro.Gracias por el consejo. Como no hay equivalente en el servidor SQL. Simplemente creé un segundo campo que convirtió el TimeSpan en ticks y lo almacené en la base de datos. Entonces evité almacenar el TimeSpan
fuente
Si no tiene que almacenar más de 24 horas, solo puede almacenar tiempo , ya que SQL Server 2008 y versiones posteriores son
time (SQL Server) <-> TimeSpan(.NET)
No se necesitan conversiones si solo necesita almacenar 24 horas o menos.
Fuente: http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
Pero , si desea almacenar más de 24 h, tendrá que almacenarlo en ticks, recuperar los datos y luego convertirlos a TimeSpan. Por ejemplo
fuente
Time
tipo SQL no pretende representar una duración, sino la parte Time de un valor DateTime; Es una elección terrible paraTimeSpan
.No hay un equivalente directo. Simplemente guárdelo numéricamente, por ejemplo, número de segundos o algo apropiado para su precisión requerida.
fuente
Sé que esta es una vieja pregunta, pero quería asegurarme de que se tengan en cuenta algunas otras opciones.
Como no puede almacenar un TimeSpan mayor de 24 horas en un campo de tipo de datos sql de tiempo; Un par de otras opciones podrían ser.
Use un varchar (xx) para almacenar el ToString del TimeSpan. El beneficio de esto es que la precisión no tiene que integrarse en el tipo de datos o el cálculo (segundos frente a milisegundos frente a días frente a quincenas) Todo lo que necesita hacer es usar TimeSpan.Parse / TryParse. Esto es lo que haría.
Use una segunda fecha, datetime o datetimeoffset, que almacena el resultado de la primera fecha + intervalo de tiempo. Leer desde la base de datos es una cuestión de TimeSpan x = SecondDate - FirstDate. El uso de esta opción lo protegerá para que otras bibliotecas que no tengan acceso a datos .NET accedan a los mismos datos pero no entiendan TimeSpans; en caso de que tengas tal ambiente.
fuente
Para ser coherente con la que probablemente sea la fuente más probable de generar un intervalo de tiempo (calculando la diferencia de 2 veces o fecha-hora), es posible que desee almacenar un .NET
TimeSpan
como unDateTime
tipo de servidor SQL .Esto se debe a que en SQL Server, la diferencia de 2
DateTime
'(Cast
aFloat
' s y luego deCast
nuevo a aDateTime
) es simplementeDateTime
relativa al 1 de enero de 1900. Ej. Una diferencia de +0.1 segundo sería el 1 de enero de 1900 00: 00: 00.100 y -0.1 segundo sería el 31 de diciembre de 1899 23: 59: 59.900.Para convertir un .NET
TimeSpan
en unDateTime
tipo de servidor SQL , primero debe convertirlo en unDateTime
tipo .NET agregándolo al 1DateTime
de enero de 1900. Por supuesto, cuando lo lea en .NET desde SQL Server, primero léalo en un .NETDateTime
y luego reste el 1 de enero de 1900 para convertirlo en .NETTimeSpan
.Para los casos de uso en los que el período de tiempo se genera desde SQL Server
DateTime
y dentro de SQL Server (es decir, a través de T-SQL) y SQL Server es anterior a 2016, dependiendo de sus necesidades de rango y precisión, puede no ser práctico almacenarlos como milisegundos (sin mencionarTicks
) porque elInt
Tipo devuelto porDateDiff
(frente a losBigInt
de SS 2016 +DateDiff_Big
) se desborda después de ~ 24 días de milisegundos y ~ 67 años. de segundos Mientras que, esta solución manejará períodos de tiempo con precisión de hasta 0.1 segundos y de -147 a +8,099 años.ADVERTENCIAS
Esto solo funcionaría si la diferencia relativa al 1 de enero de 1900 daría como resultado un valor dentro del rango de un
DateTime
tipo de servidor SQL (1 de enero de 1753 al 31 de diciembre de 9999, también conocido como -147 a +8,099 años). No tenemos que preocuparnos tanto por elTimeSpan
lado de .NET , ya que puede contener ~ 29 k a +29 k años. No mencioné el servidor SQLDateTime2
(cuyo rango, en el lado negativo, es mucho mayor que el de SQL ServerDateTime
), porque: a) no se puede convertir a un valor numérico a través de un simpleCast
yb)DateTime
el rango debería ser suficiente para la gran mayoría de los casos de uso.SQL Server
DateTime
diferencias calculadas a través de laCast
- a -Float
- y - método de respaldo no parece ser precisa más allá de 0,1 segundos.fuente
Hay varias formas de presentar un intervalo de tiempo en la base de datos.
hora
Este tipo de datos es compatible desde SQL Server 2008 y es la forma preferida de almacenar un
TimeSpan
. No se necesita mapeo. También funciona bien con código SQL.Sin embargo, como se indicó en la pregunta original, este tipo de datos está limitado a 24 horas.
datetimeoffset
El
datetimeoffset
tipo de datos se asigna directamente aSystem.DateTimeOffset
. Se usa para expresar el desplazamiento entre adatetime
/datetime2
a UTC, pero también puede usarlo paraTimeSpan
.Sin embargo, dado que el tipo de datos sugiere una semántica muy específica, también debe considerar otras opciones.
datetime / datetime2
Un enfoque podría ser usar los tipos
datetime
odatetime2
. Esto es mejor en escenarios donde necesita procesar los valores en la base de datos directamente, es decir. para vistas, procedimientos almacenados o informes. El inconveniente es que debe restar el valorDateTime(1900,01,01,00,00,00)
de la fecha para recuperar el intervalo de tiempo en su lógica empresarial.Empezando
Otro enfoque podría ser convertir el TimeSpan en ticks y usar el
bigint
tipo de datos. Sin embargo, este enfoque tiene el inconveniente de que es engorroso usarlo en consultas SQL.varchar (N)
Esto es mejor para los casos en que el valor debería ser legible por los humanos. También puede usar este formato en consultas SQL utilizando la
CONVERT(datetime, ValidityPeriod)
función. Dependiendo de la precisión requerida, necesitará entre 8 y 25 caracteres.Bonificación: período y duración
Usando una cadena, también puede almacenar tipos de datos NodaTime , especialmente
Duration
yPeriod
. El primero es básicamente lo mismo que un TimeSpan, mientras que el segundo respeta que algunos días y meses son más largos o más cortos que otros (es decir, enero tiene 31 días y febrero tiene 28 o 29; algunos días son más largos o más cortos debido al horario de verano ) En tales casos, usar un TimeSpan es la elección incorrecta.Puede usar este código para convertir períodos:
Y luego úsalo como
Me gusta mucho
NodaTime
y, a menudo, me salva de errores difíciles y mucho dolor de cabeza. El inconveniente aquí es que realmente no puede usarlo en consultas SQL y necesita hacer cálculos en la memoria.Tipo definido por el usuario CLR
También tiene la opción de usar un tipo de datos personalizado y admitir una
TimeSpan
clase personalizada directamente. Consulte Tipos definidos por el usuario de CLR para más detalles.El inconveniente aquí es que el tipo de datos podría no comportarse bien con los informes SQL. Además, algunas versiones de SQL Server (Azure, Linux, Data Warehouse) no son compatibles.
Conversiones de valor
A partir de EntityFramework Core 2.1, tiene la opción de usar Conversiones de valor .
Sin embargo, al usar esto, EF no podrá convertir muchas consultas en SQL, lo que hará que las consultas se ejecuten en la memoria; potencialmente transfiriendo montones y montones de datos a su aplicación.
Entonces, al menos por ahora, podría ser mejor no usarlo, y simplemente mapear el resultado de la consulta con Automapper .
fuente
Por lo general, almaceno un TimeSpan como un bigint poblado con ticks de la propiedad TimeSpan.Ticks como se sugirió anteriormente. También puede almacenar un TimeSpan como varchar (26) poblado con la salida de TimeSpan.ToString (). Las cuatro funciones escalares (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks) que escribí son útiles para manejar TimeSpan en el lado de SQL y evitar los ataques que producirían rangos limitados artificialmente. Si puede almacenar el intervalo en un .NET TimeSpan, también debería funcionar con estas funciones. Además, las funciones le permiten trabajar con TimeSpans y ticks de 100 nanosegundos, incluso cuando se utilizan tecnologías que no incluyen .NET Framework.
fuente