AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 341943
Accepted
Mark Storey-Smith
Mark Storey-Smith
Asked: 2024-08-27 22:21:13 +0800 CST2024-08-27 22:21:13 +0800 CST 2024-08-27 22:21:13 +0800 CST

Arredondamento DATEDIFF

  • 772

Implementando um esquema de partição rotativa, por kejser.org/table-pattern-rotating-log-ring- . Tive um problema com DATEDIFF arredondando valores:

DECLARE @Partitions INT = 15;

SELECT
    a1.dt
    , dtTrunc 
    , dtDiff
    , PartitionKey = CAST(DATEDIFF(DAY, 0, dtDiff) % @Partitions AS TINYINT)
FROM
    (
    VALUES
        ('2024-08-17 23:59:59.997')
        , ('2024-08-17 23:59:59.998')
        , ('2024-08-17 23:59:59.999')
        , ('2024-08-18 00:00:00.000')
    )
    AS v(dt)
CROSS APPLY
    (
    SELECT
        dt = CAST(v.dt AS DATETIME2(3))
    ) a1
CROSS APPLY
    (
    SELECT
        dtTrunc = CAST(a1.dt AS DATE)
        , dtDiff = DATEDIFF(day, 0, a1.dt)
    ) a2

Consulta 1 Saída

Problema resolvido com elenco até o momento:

DECLARE @Partitions INT = 15;

SELECT
    a1.dt
    , dtTrunc 
    , dtDiff
    , PartitionKey = CAST(DATEDIFF(DAY, 0, dtDiff) % @Partitions AS TINYINT)
FROM
    (
    VALUES
        ('2024-08-17 23:59:59.997')
        , ('2024-08-17 23:59:59.998')
        , ('2024-08-17 23:59:59.999')
        , ('2024-08-18 00:00:00.000')
    )
    AS v(dt)
CROSS APPLY
    (
    SELECT
        dt = CAST(v.dt AS DATETIME2(3))
    ) a1
CROSS APPLY
    (
    SELECT
        dtTrunc = CAST(a1.dt AS DATE)
        , dtDiff = DATEDIFF(day, 0, CAST(a1.dt AS DATE))
    ) a2

Saída da Consulta 2

Esse é um comportamento esperado/documentado? Se sim, onde?

sql-server
  • 2 2 respostas
  • 693 Views

2 respostas

  • Voted
  1. Paul White
    2024-08-28T18:44:44+08:002024-08-28T18:44:44+08:00

    Esse é um comportamento esperado/documentado?

    Isso só é esperado se você estiver familiarizado com as regras, mas elas nunca foram totalmente documentadas .

    Este é particularmente o caso para o legado datetimee smalldatetimetipos, que têm uma série de peculiaridades mantidas para compatibilidade com versões anteriores. Os tipos mais modernos como datetime2não permitem estranhezas como adicionar números a datas ou converter implicitamente inteiros. Muitas das peculiaridades são o resultado de decisões questionáveis ​​do passado.

    Se quiser evitar surpresas, seja sempre explícito sobre os tipos de dados.

    A desleixo de tipo é abundante no código original. Strings não são tipos de data/hora, elas são strings. CASTnão pode aceitar um parâmetro de estilo (ao contrário de CONVERT), o que pode resultar em ambiguidade, resultados não determinísticos ou até mesmo erros de tempo de execução. Usar DATEDIFFcom uma mistura de tipos de dados é apenas pedir problemas.

    Exemplo

    Vamos pegar o exemplo de Jonathan Fite porque é mais simples que o original:

    SELECT
        v.dt,
        UsingZero = DATEDIFF(DAY, 0, v.dt),
        ConvertToDateTime = DATEDIFF(DAY, CONVERT(datetime, 0), v.dt),
        MishMash = DATEDIFF(DAY, CONVERT(datetime, 0), CONVERT(datetime2(3), v.dt))
    FROM
    (
        VALUES
            ('2024-08-17 23:59:59.997'),
            ('2024-08-17 23:59:59.998'),
            ('2024-08-17 23:59:59.999'),
            ('2024-08-18 00:00:00.000')
    ) AS v (dt);
    

    Os itens na VALUEScláusula são strings. Eles podem parecer datas e horas para um humano, mas não são.

    As expressões na SELECTcláusula estão à mercê das regras misteriosas para conversão de tipo de dados executadas pelas DATEDIFFimplementações (sim, existem várias).

    1.UsingZero = DATEDIFF(DAY, 0, v.dt)

    Aqui chamamos DATEDIFFcom um inteiro e uma varcharstring como parâmetros.

    O SQL Server converte tanto o zero quanto a dtstring para datetime, como você pode ver no plano de execução:

    Expr1005 = datediff(day,'1900-01-01 00:00:00.000',
      CONVERT_IMPLICIT(datetime,[Union1004],0))
    

    Union1004é o rótulo dado às varcharstrings da VALUEScláusula. O literal '1900-01-01 00:00:00.000' é o resultado dobrado constante da conversão de zero para datetime. Observe que o formato da string exibida é datetime-specific (ela tem três decimais fracionários).

    A conversão implícita de suas strings para datetimeé a parte importante para explicar sua preocupação com arredondamento:

    SELECT
        v.dt,
        AsDateTime = CONVERT(datetime, v.dt, 121)
    FROM
    (
        VALUES
            -- These are strings
            ('2024-08-17 23:59:59.997'),
            ('2024-08-17 23:59:59.998'),
            ('2024-08-17 23:59:59.999'),
            ('2024-08-18 00:00:00.000')
    ) AS v (dt);
    
    dt ComoDataHora
    2024-08-17 23:59:59.997 2024-08-17 23:59:59.997
    2024-08-17 23:59:59.998 2024-08-17 23:59:59.997
    2024-08-17 23:59:59.999 2024-08-18 00:00:00.000
    2024-08-18 00:00:00.000 2024-08-18 00:00:00.000

    Observe o arredondamento devido à precisão limitada de datetime(1/300s).

    Não é o zero que causa o arredondamento, é a conversão implícita de suas strings para datetime.

    2.ConvertToDateTime = DATEDIFF(DAY, CONVERT(datetime, 0), v.dt)

    Agora fornecemos DATEDIFFum datetimevalor e uma varcharstring.

    O SQL Server converte ambos para datetimeoffset, como você pode ver no plano de execução:

    Expr1006 = datediff(day,'1900-01-01 00:00:00.000 +00:00',
      CONVERT_IMPLICIT(datetimeoffset(7),[Union1004],0))
    

    O literal '1900-01-01 00:00:00.000 +00:00' é o resultado dobrado constante da conversão de zero para datetimeoffset(3). O formato da string exibida tem um deslocamento de fuso horário e três casas decimais. Sua string é convertida implicitamente para datetimeoffset(7).

    Nenhum arredondamento ocorre neste caso porque o zero mapeia para um valor de data exato e todas as strings são exatamente representáveis.

    3.MishMash = DATEDIFF(DAY, CONVERT(datetime, 0), CONVERT(datetime2(3), v.dt))

    Por fim, fornecemos DATEDIFFum datetimee o resultado da conversão das varcharstrings para datetime2(3)(com um estilo padrão).

    O plano de execução mostra o cast explícito para datetimeconstant-folded para datetimeoffset(3)e exibido com os três decimais esperados e um deslocamento de fuso horário. Suas varcharstrings são primeiro cast explicitamente para datetime2(3)conforme solicitado pelo código, então implicitamente convertidas para datettimeoffset(3):

    Expr1007 = datediff(day,'1900-01-01 00:00:00.000 +00:00',
      CONVERT_IMPLICIT(datetimeoffset(3),CONVERT(datetime2(3),[Union1004],0),0))
    

    Provavelmente não era isso que pretendiam.

    Sendo explícito

    Você não disse qual era o tipo de dado dos valores de origem, mas tendo em mente DATEDIFFos usos datetimeoffsetinternos para a maioria dos cálculos, podemos muito bem escolher:

    SELECT
        V.dt,
        Typed =
            DATEDIFF
            (
                DAY,
                -- Explicit type
                CONVERT(datetimeoffset(3), '1900-01-01T00:00:00.000Z', 127),
                V.dt
            )
    FROM
    (
        VALUES
            -- Explicit types
            (CONVERT(datetimeoffset(3), '2024-08-17T23:59:59.997Z', 127)),
            (CONVERT(datetimeoffset(3), '2024-08-17T23:59:59.998Z', 127)),
            (CONVERT(datetimeoffset(3), '2024-08-17T23:59:59.999Z', 127)),
            (CONVERT(datetimeoffset(3), '2024-08-18T00:00:00.000Z', 127))
    ) AS V (dt);
    

    Resultados:

    dt Digitado
    17/08/2024 23:59:59.997 +00:00 45519
    17/08/2024 23:59:59.998 +00:00 45519
    17/08/2024 23:59:59.999 +00:00 45519
    18/08/2024 00:00:00.000 +00:00 45520

    O plano de execução mostra:

    [Expr1005] = datediff(day,'1900-01-01 00:00:00.000 +00:00',[Union1004])
    

    Não é tão fácil de ver, mas os [Union1004]valores também têm o tipo correto:

    ('2024-08-17 23:59:59.997 +00:00'), 
    ('2024-08-17 23:59:59.998 +00:00'), 
    ('2024-08-17 23:59:59.999 +00:00'), 
    ('2024-08-18 00:00:00.000 +00:00')
    

    Você pode escolher datetime2(3)ou qualquer coisa para corresponder exatamente ao tipo de dado de origem, mas então haverá uma conversão implícita no datetimeoffsetplano porque é isso que DATEDIFFé usado internamente nesse caso.

    Conselho

    Escolha um estilo determinístico apropriado para conversões de data/hora de strings, prefira CONVERTover CAST(que não aceita um estilo) e seja explícito sobre seus tipos de dados se não gostar de resultados confusos e situações difíceis de depurar em geral.

    • 15
  2. Best Answer
    Jonathan Fite
    2024-08-27T23:32:44+08:002024-08-27T23:32:44+08:00

    Fiz uma pergunta relevante aqui. O que 0 em DATEDIFF(MINUTE, 0, <Date>) realmente significa?

    Também implementei o mesmo esquema de rotação de logs no meu ambiente de produção e adoraria vê-lo mais usado! (grande fã de Thomas Kejser, pena que o site dele só esteja disponível no arquivo da web).

    O problema que você está enfrentando é devido a como o SQL lida com o 0 quando usado como partes de datediff, ele não converte para o tipo esperado. Se você fizer uma conversão explícita para datetime (não pode fazer datetime2, a propósito), você obtém o resultado esperado. Eu também sugeriria uma conversão explícita do tipo table para o tipo desejado, mas isso provavelmente não está impactando sua solução real.

    SELECT v.dt
        , DATEDIFF(DAY, 0, v.dt) AS UsingZero
        , DATEDIFF(DAY, CONVERT(DATETIME, 0), v.dt) AS ConvertToDateTime
        , DATEDIFF(DAY, CONVERT(DATETIME, 0), CONVERT(DATETIME2(3), v.dt))
    FROM (
        VALUES
            ('2024-08-17 23:59:59.997')
            , ('2024-08-17 23:59:59.998')
            , ('2024-08-17 23:59:59.999')
            , ('2024-08-18 00:00:00.000')
        )
        AS v(dt)
    
    • 6

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve