Estou tentando armazenar um .Net TimeSpan
no SQL server 2008 R2.
O código EF First parece sugerir que ele deve ser armazenado como Time(7)
no SQL.
No entanto TimeSpan
, o .Net pode lidar com períodos maiores que 24 horas.
Qual é a melhor maneira de lidar com o armazenamento de .Net TimeSpan
no SQL server?
.net
sql-server
timespan
GraemeMiller
fonte
fonte
Respostas:
Eu o armazenaria no banco de dados como
BIGINT
e armazenaria o número de ticks (por exemplo, propriedade TimeSpan.Ticks ).Dessa forma, se eu quisesse obter um objeto TimeSpan ao recuperá-lo, poderia fazer TimeSpan.FromTicks (value) o que seria fácil.
fonte
SELECT CAST(DATEADD(MILLISECOND, @Ticks/CAST(10000 AS BIGINT), '1900-01-01') AS TIME)
. A'1900-01-01'
data não importa, é claro, é apenas a terceira variável requerida pelaDATEADD(...)
função. Lembre-se de que há 100 nanossegundos em um tick, mas se você usar,DATEADD(NANOSECOND...
é provável que haja um estouro, portanto, usando milissegundos. Lembre-se também de que você deve verificar esse fato usando C #TimeSpan.TicksPerMillisecond
(deve ser 10000) para ter certeza.Obrigado pelo conselho. Como não há equivalente no servidor SQL. Eu simplesmente criei um segundo campo que convertia o TimeSpan em ticks e o armazenava no banco de dados. Impedi então o armazenamento do TimeSpan
fonte
Se você não precisar armazenar mais de 24 horas, poderá apenas armazenar o tempo , desde o SQL Server 2008 e posterior, o mapeamento é
time (SQL Server) <-> TimeSpan(.NET)
Não são necessárias conversões se você precisar armazenar apenas 24 horas ou menos.
Fonte: http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
Mas , se você deseja armazenar mais de 24 horas, precisará armazená-lo em ticks, recuperar os dados e depois converter para TimeSpan. Por exemplo
fonte
Time
tipo SQL não representa uma duração, mas a parte Time de um valor DateTime; é uma péssima escolha paraTimeSpan
.Não há um equivalente direto. Apenas armazene-o numericamente, por exemplo, número de segundos ou algo apropriado à sua precisão necessária.
fonte
Sei que essa é uma pergunta antiga, mas queria garantir que outras duas opções fossem anotadas.
Como você não pode armazenar um TimeSpan maior que 24 horas em um campo de tipo de dados sql de tempo; algumas outras opções podem ser.
Use um varchar (xx) para armazenar o ToString do TimeSpan. O benefício disso é que a precisão não precisa ser inserida no tipo de dados ou no cálculo (segundos x milissegundos x dias x quinzenas) Tudo que você precisa é usar TimeSpan.Parse / TryParse. Isto é o que eu faria.
Use uma segunda data, datetime ou datetimeoffset, que armazene o resultado da primeira data + intervalo de tempo. A leitura do banco de dados é uma questão de TimeSpan x = SecondDate - FirstDate. O uso dessa opção o protegerá para que outras bibliotecas de acesso a dados não .NET acessem os mesmos dados, mas não entendam o TimeSpans; caso você tenha esse ambiente.
fonte
Para ser consistente com o que provavelmente é a fonte mais provável de gerar um intervalo de tempo (calculando a diferença de 2 vezes ou data e hora), convém armazenar um .NET
TimeSpan
como um tipo de SQL ServerDateTime
.Isso ocorre porque no SQL Server, a diferença de 2
DateTime
'(Cast
toFloat
' e depoisCast
volta para aDateTime
) é simplesmente umaDateTime
relativa a 1º de janeiro de 1900. Ex. Uma diferença de +0,1 segundo seria 1º de janeiro de 1900 00: 00: 00.100 e -0,1 segundo seria 31 de dezembro de 1899 23: 59: 59.900.Para converter um .NET
TimeSpan
em umDateTime
tipo de servidor SQL , primeiro você o converteria em umDateTime
tipo .NET adicionando-o a 1DateTime
de janeiro de 1900. É claro que, quando você o lê no .NET a partir do SQL Server, primeiro leia-o em um .NETDateTime
e subtraia em 1º de janeiro de 1900 para convertê-lo em .NETTimeSpan
.Para casos de uso em que os intervalos de tempo estão sendo gerados a partir do SQL Server
DateTime
e dentro do SQL Server (ou seja, via T-SQL) e o SQL Server é anterior a 2016, dependendo de suas necessidades de alcance e precisão, pode não ser prático armazená-las como milissegundos (para não mencionarTicks
) porque oInt
Tipo retornado porDateDiff
(vs. oBigInt
do SS 2016 +DateDiff_Big
) excede após aproximadamente 24 dias em milissegundos e aproximadamente 67 anos. de segundos. Visto que esta solução lidará com intervalos de tempo com precisão de até 0,1 segundos e de -147 a +8,099 anos.ADVERTÊNCIAS:
Isso só funcionaria se a diferença em relação a 1º de janeiro de 1900 resultasse em um valor dentro do intervalo de um
DateTime
tipo de servidor SQL (1º de janeiro de 1753 a 31 de dezembro de 9999, também conhecido como -147 a +8.099 anos). Não precisamos nos preocupar tanto quanto oTimeSpan
lado .NET , pois ele pode armazenar de 29 a +29 k anos. Não mencionei o SQL ServerDateTime2
Type (cujo intervalo, no lado negativo, é muito maior que o do SQL ServerDateTime
), porque: a) não pode ser convertido em numérico por meio de um simplesCast
eb)DateTime
deve ser suficiente para a grande maioria dos casos de uso.SQL Server
DateTime
diferenças calculadas através doCast
- de -Float
- e - método de volta não parece ser precisos para além 0,1 segundos.fonte
Existem várias maneiras de apresentar um intervalo de tempo no banco de dados.
Tempo
Esse tipo de dados é suportado desde o SQL Server 2008 e é a maneira preferida de armazenar um
TimeSpan
. Não há mapeamento necessário. Também funciona bem com o código SQL.No entanto, conforme declarado na pergunta original, esse tipo de dados é limitado a 24 horas.
datetimeoffset
O
datetimeoffset
tipo de dados é mapeado diretamente paraSystem.DateTimeOffset
. É usado para expressar o deslocamento entre adatetime
/datetime2
UTC, mas você também pode usá-lo paraTimeSpan
.No entanto, como o tipo de dados sugere uma semântica muito específica, você também deve considerar outras opções.
datetime / datetime2
Uma abordagem pode ser usar os tipos
datetime
oudatetime2
. Isso é melhor nos cenários em que você precisa processar os valores no banco de dados diretamente, ou seja. para visualizações, procedimentos armazenados ou relatórios. A desvantagem é que você precisa subtrair o valorDateTime(1900,01,01,00,00,00)
da data para recuperar o período de tempo em sua lógica de negócios.bigint
Outra abordagem pode ser converter o TimeSpan em ticks e usar o
bigint
tipo de dados. No entanto, essa abordagem tem a desvantagem de ser difícil de usar em consultas SQL.varchar (N)
Isso é melhor para casos em que o valor deve ser legível por humanos. Você também pode usar esse formato em consultas SQL utilizando a
CONVERT(datetime, ValidityPeriod)
função Dependendo da precisão exigida, você precisará entre 8 e 25 caracteres.Bônus: Período e Duração
Usando uma string, você também pode armazenar tipos de dados NodaTime , especialmente
Duration
ePeriod
. O primeiro é basicamente o mesmo que um TimeSpan, enquanto o segundo respeita que alguns dias e meses são mais longos ou mais curtos que outros (ou seja, janeiro tem 31 dias e fevereiro tem 28 ou 29; alguns dias são mais longos ou mais curtos devido ao horário de verão ) Nesses casos, o uso de um TimeSpan é a escolha errada.Você pode usar este código para converter períodos:
E então use-o como
Eu realmente gosto
NodaTime
e muitas vezes me salva de insetos complicados e muita dor de cabeça. A desvantagem aqui é que você realmente não pode usá-lo em consultas SQL e precisa fazer cálculos na memória.Tipo definido pelo usuário CLR
Você também tem a opção de usar um tipo de dados personalizado e suportar
TimeSpan
diretamente uma classe personalizada . Consulte Tipos definidos pelo usuário do CLR para obter detalhes.A desvantagem aqui é que o tipo de dados pode não se comportar bem com os Relatórios SQL. Além disso, algumas versões do SQL Server (Azure, Linux, Data Warehouse) não são suportadas.
Conversões de valor
A partir do EntityFramework Core 2.1, você tem a opção de usar Conversões de Valor .
No entanto, ao usar isso, o EF não poderá converter muitas consultas em SQL, fazendo com que as consultas sejam executadas na memória; potencialmente transferir muitos e muitos dados para seu aplicativo.
Portanto, pelo menos por enquanto, é melhor não usá-lo e apenas mapeie o resultado da consulta com o Automapper .
fonte
Normalmente, eu armazeno um TimeSpan como um bigint preenchido com tiques da propriedade TimeSpan.Ticks, conforme sugerido anteriormente. Você também pode armazenar um TimeSpan como um varchar (26) preenchido com a saída de TimeSpan.ToString (). As quatro funções escalares (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks) que escrevi são úteis para lidar com o TimeSpan no lado SQL e evitar os hacks que produziriam intervalos limitados artificialmente. Se você pode armazenar o intervalo em um .NET TimeSpan, ele deve funcionar com essas funções também. Além disso, as funções permitem que você trabalhe com TimeSpans e ticks de 100 nanossegundos, mesmo ao usar tecnologias que não incluem o .NET Framework.
fonte