Como adicionar 1 milissegundo a uma string de data e hora?

15

Com base em uma seleção, posso retornar x linhas como esta:

1   2019-07-23 10:14:04.000
1   2019-07-23 10:14:11.000
2   2019-07-23 10:45:32.000
1   2019-07-23 10:45:33.000

Temos todos os milissegundos com 0.

Existe uma maneira de adicionar 1 por 1 milissegundos, para que o select fique assim:

1   2019-07-23 10:14:04.001
1   2019-07-23 10:14:11.002
2   2019-07-23 10:45:32.003
1   2019-07-23 10:45:33.004

Estou tentando criar um cursor ou mesmo uma atualização sem sucesso.

Esta é a consulta para obter os resultados desejados:

  select top 10 ModifiedOn 
    from [SCHEMA].[dbo].[TABLE]
  where FIELD between '2019-07-23 00:00' and '2019-07-23 23:59'

Existem 81k valores. O campo é DATETIME.

Racer SQL
fonte
2
Você está tentando adicionar 1 milissegundo à linha 1, 2 milissegundos à linha 2, 3 milissegundos à linha 3, etc.?
John Eisbrener 23/07/19

Respostas:

33

Datetimenão é preciso para o nível de 1 milissegundo. O que você está pedindo não é possível, a menos que você mude para um tipo de dados diferente (ou seja datetime2).

Documentação

Citação importante:

Precisão Arredondado para incrementos de 0,000, 0,003 ou 0,007 segundos

Forrest
fonte
13

A DateAddfunção é o que você está procurando.

Use millisecondcomo o primeiro parâmetro para a função, para informar que você está adicionando milissegundos. Em seguida, use 1como o segundo parâmetro, para adicionar o número de milissegundos.

Aqui está um exemplo, capturando o tempo atual em uma variável e, em seguida, adicionando um milissegundo a ele e salvando o resultado como uma segunda variável e imprimindo cada variável

Declare @RightNow as DateTime2
Declare @RightNowPlusAMillisecond as DateTime2

Select @RightNow = Getdate()
Select @RightNowPlusAMillisecond = DateAdd(millisecond,1,@RightNow)

Print @RightNow
Print @RightNowPlusAMillisecond

Resultados:

2019-07-23 08:25:38.3500000
2019-07-23 08:25:38.3510000

Nota:

Como Forrest aponta em outra resposta, o datetimetipo de dados não garante precisão de milissegundos. Arredonda para incrementos de 0,000, 0,003 ou 0,007 segundos. Se você quiser precisão de milissegundos, use datetime2.

Doug Deden
fonte
13

@ Doug-Deden tem o ponto de partida certo, mas eu só queria tentar responder o que eu pensava ser a intenção original da pergunta - como aplicá-la a um conjunto de resultados com milissegundos por linha cada vez maior.

Nesse caso, você pode usar ROW_NUMBER e uma expressão de tabela comum (edite conforme necessário para sua estrutura de tabela, incluindo junções, etc.).

Selecione para mostrar valores:

;WITH CTE AS (
SELECT t.my_id, t.my_date_column, ROW_NUMBER() OVER (ORDER BY my_date_column, my_id DESC) AS R
FROM Table1 t
)
SELECT TOP 1000 *, DATEADD(MILLISECOND, R, CAST(my_date_column AS datetime2)) [new_date]
FROM CTE
ORDER BY my_date_column

A atualização volta à tabela original:

;WITH CTE AS (
SELECT t.my_id, t.my_date_column, ROW_NUMBER() OVER (ORDER BY my_date_column, my_id DESC) AS R
FROM Table1 t
)
UPDATE t SET 
my_date_column = DATEADD(MILLISECOND, R, CAST(my_date_column AS datetime2))
FROM CTE c
     JOIN Table1 t ON c.my_id = t.my_id
BlueGI
fonte
Este CTE é atualizável. Não há necessidade de voltar a participar Table1. Apenas façaUPDATE CTE SET my_date_column =...
Steven Hibble
4

Eu fiz isso usando DATETIME2(3).

Como você pode ver na consulta abaixo, é mais economic:

declare @dt1 datetime2(3)
declare @dt2 datetime2

SELECT @DT1 = SYSDATETIME()
SELECT @DT2=  SYSDATETIME()

SELECT [THE LENGTH OF DATETIME2]=DATALENGTH(@DT2)
      ,[THE LENGTH OF DATETIME2(3)]=DATALENGTH(@DT1)

insira a descrição da imagem aqui

As diferenças entre datetimee datetime2são bem explicadas aqui .

Para este exercício, crio uma tabela temporária para fins de teste e a preencho com 999 diferentes random datesde 01-jan-2019e hoje ( 23-july-2019)

e, em seguida, defino os milissegundos de 1 a 999

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SET NOEXEC OFF

IF OBJECT_ID ('TEMPDB..#T1') IS NOT NULL
   DROP TABLE #T1

CREATE TABLE #t1(the_date DATETIME2(3) NOT NULL PRIMARY KEY CLUSTERED )
GO

-- run this 999 times - hopefully there will be no duplicates
-- SELECT 204*24*60*60 - today is 23-july-2019 - the 203rd day of the year
    DECLARE @DT DATETIME2(3)
    SELECT @DT = CONVERT(DATETIME2(3),
           DATEADD(SECOND, ABS(CHECKSUM(NEWID()) % 17625600), 
                   '2019-01-01'),120) 

    --SELECT @DT

    IF NOT EXISTS( SELECT 1 FROM #T1 WHERE THE_DATE = @DT) 
    INSERT INTO #T1 VALUES (@DT)
GO 999


--check it out what we have
SELECT * FROM #T1

--get the date and the new date
SELECT 
 THE_DATE
,THE_NEW_DATE= DATEADD(MILLISECOND, ROW_NUMBER() OVER (ORDER BY THE_DATE), THE_DATE ) 
 FROM #T1

e é isso que eu recebo: (vista parcial)

insira a descrição da imagem aqui

Marcello Miorelli
fonte
2

Um dos outros pôsteres está correto; DATETIME(em T-SQL) não é preciso até o milissegundo (é preciso até o centésimo de segundo).

Para esse nível de precisão, você deseja usar DATETIME2.

Aqui está um exemplo de conversão de uma string datetimepara datetime2, em seguida, adicionando 1 milissegundo e, por último, convertendo novamente em uma string.

select convert(
            varchar(MAX), --in T-SQL, varchar length is optional
            dateadd(
                millisecond,
                1,
                convert(
                    datetime2,
                    '2019-07-23 12:01:23.11'
                )
            )
        )
McDonalds Happy Meal
fonte