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 / 319890
Accepted
CaM
CaM
Asked: 2022-11-19 07:04:26 +0800 CST2022-11-19 07:04:26 +0800 CST 2022-11-19 07:04:26 +0800 CST

SQL Modulo Function dá valor errado?

  • 772

Em Javascript, Excel, Python e uma calculadora científica, a operação de módulo

-34086.1576962834 % 360.0      <-- python
=MOD(-34086.1576962834, 360.0) <-- Excel function

dá um valor de 113.84230371659942. (Ou algo muito próximo disso, dependendo do número de casas decimais e arredondamento.)

Mas no T-SQL,

Select -34086.1576962834 % 360.0

retorna um valor de -246.1576962834.

eu tentei usar

Select cast(-34086.1576962834 as numeric(30,15)) % cast(360.0 as numeric(30,15))

Como eu estava pensando, talvez este fosse um exemplo de flutuação versus matemática decimal fixa que não correspondia, mas isso não resolve.

Por que o SQL Server não executa esta operação de maneira consistente com as calculadoras/resultados esperados? E mais importante, existe uma maneira de fazer o T-SQL fazer aritmética da mesma forma que minha calculadora faz?

Estou usando principalmente o SQL Server 2019, mas confirmei que o SQL Server 2012 e 2014 também fornecem os mesmos -246... resultados.

A função Oracle mod(x,y)e o python math.fmod(x,y)fornecem o mesmo resultado -246.nnn.

sql-server
  • 2 2 respostas
  • 646 Views

2 respostas

  • Voted
  1. Best Answer
    bbaird
    2022-11-19T07:26:42+08:002022-11-19T07:26:42+08:00

    De acordo com a Wikipedia , o padrão SQL implementa a versão truncada do %operador que para um número a = nq + r onde q é um número inteiro, define o resto r como:

    r = a - n [ a / n ] onde [] é a função de parte inteira (parte inteira) 1

    Portanto, inserir o exemplo na equação resulta

    r = -34086.1576962834 - 360.0[-34086.1576962834/360.0] 
    
      = -34086.1576962834 - 360.0[-94.68377138]
    
      = -34086.1576962834 - 360.0(-94)
    
      = -246.1576962834
    

    Portanto, de acordo com o padrão SQL, a %função está funcionando conforme o esperado. Consulte Postgres e MySQL implementando o padrão de maneira semelhante.

    Se você deseja obter o mesmo resultado de um método que usa divisão horizontal em vez da parte integral quando o dividendo é negativo, você pode adicionar o divisor ao resultado como [ x ] + 1 = FLOOR( x ) quando x é negativo .

    r + n = a - n[a/n] + n
    
          = a - n([a/n] + 1) 
    
          = a - n(FLOOR(a/n))
    

    Ou, conforme demonstrado neste SO, a resposta (n + a % n) % n) converterá para o método de divisão por piso, independentemente de o dividendo ser positivo ou negativo 2 .


    Isto é o que o SQL Standard tem a dizer: (graças a ypercubeᵀᴹ e esta postagem no blog ):

    <modulus expression> ::=
      MOD <left paren>
          <numeric value expression dividend>
          <comma>
          <numeric value expression divisor>
          <right paren>
    ...
    If <modulus expression> is specified,
    then the declared type of each
    <numeric value expression>
    shall be exact numeric with scale 0 (zero).
    The declared type of the result is the
    declared type of the immediately contained
    <numeric value expression divisor>.
    ...
    9)If <modulus expression> is specified,
      then let N be the value of the immediately
      contained <numeric value expression
      dividend> and let M be the value of the
      immediately contained <numeric value
      expression divisor>.
      Case:
        a)If at least one of N and M is the null
          value, then the result is the null value.
        b)If M is zero,
          then an exception condition is raised:
          data exception—division by zero.
        c)Otherwise, the result is the unique exact
          numeric value R with scale 0 (zero) such
          that all of the following are true:
          i)R has the same sign as N.
          ii)The absolute value of R is less than
             the absolute value of M.
          iii)N = M * K + R for some exact numeric 
              value K with scale 0 (zero).
    

    Portanto, você notará muita "escala de zero", mas isso não é relevante, pois se removermos essa restrição de N , M e R , o cálculo permanecerá inalterado. Movendo as coisas temos:

    R = N - M * K com || R || < || M || e SIGN( R ) = SIGN( N ).

    Isso não é math.stackexchange.com , então não farei uma prova formal, mas essas duas restrições implicam que K nessa instância é [ N / M ] e não FLOOR( N / M ).

    Usando seu exemplo temos então:

    R = -34086.1576962834 - 360.0*(-94)
      = -246.1576962834
    

    1 Isso não é o mesmo que a FLOORfunção quando aplicada a valores negativos.

    2 A prova é deixada como um exercício para o leitor porque o autor não toca em um livro de matemática há mais de 10 anos.

    • 13
  2. AMtwo
    2022-11-19T07:34:09+08:002022-11-19T07:34:09+08:00

    Isso parece ser um problema de como o SQL Server calcula o módulo com números negativos.

    Você notará que, se pegar sua calculadora científica (estou usando o Windows embutido calc.exeno modo científico) e fizer as contas para um quociente positivo e um quociente negativo, obterá 113.nnn e 246. números nnn: Calculadora

    Se dividirmos a matemática do módulo e fizermos "o caminho mais longo", poderemos ver o que o SQL Server está fazendo:

    DECLARE @dividend numeric(30,15) = -34086.1576962834;
    DECLARE @divisor numeric(30,15)  = 360.0;
    
    SELECT QuotientFloor          = FLOOR(@dividend / @divisor),
           QuotientCeiling        = CEILING(@dividend / @divisor),
           Remainder              = @dividend % @divisor,
           DividendInput          = @dividend,
           DivisorInput           = @divisor,
           QuotientFloorPlusMod   = ( FLOOR(@dividend / @divisor) * @divisor ) + (@dividend % @divisor),
           QuotientCeilingPlusMod = ( CEILING(@dividend / @divisor) * @divisor ) + (@dividend % @divisor);
    

    IMHO, isso é um bug

    O SQL Server está sempre fazendo um FLOORpara calcular o quociente, em vez de sempre arredondar para zero. Na minha opinião, isso é um bug, pois as regras da matemática nos dizem que devemos arredondar para zero, em vez de arredondar para baixo.

    O artigo da Wikipedia sobre "operação de módulo" mostra que existem diferentes interpretações do que "módulo" significa, particularmente quando se move para números reais - embora os números naturais tenham uma definição de módulo mais universalmente compreendida.

    De acordo com o artigo da Wikipedia vinculado acima, o padrão ISO SQL exige a fórmula de "divisão truncada":

    Muitas implementações usam divisão truncada, para a qual o quociente é definido por fórmula de quociente de divisão truncada onde [] é a função de parte integral (arredondamento para zero), ou seja, o truncamento para zero dígitos significativos. Assim, de acordo com a equação (1), o resto tem o mesmo sinal do dividendo: fórmula Mod de divisão truncada

    Observe que isso exige arredondamento para zero . Este é o comportamento que a maioria das pessoas espera, porque o arredondamento para zero parece "natural" para muitas pessoas ao arredondar.

    Em vez disso, o SQL Server usa o método "divisão por piso":

    Donald Knuth promove a divisão floored, para a qual o quociente é definido por fórmula do quociente de divisão pavimentada onde ⌊⌋ é a função floor (arredondamento para baixo). Assim, de acordo com a equação (1), o resto tem o mesmo sinal do divisor: fórmula mod divisão pavimentada

    Observe que isso exige arredondamento para baixo .

    A Microsoft provavelmente não mudará o comportamento

    O SQL Server tem esse comportamento há tanto tempo que não esperaria que o SQL Server mudasse sua implementação para usar a fórmula de "divisão truncada" exigida pelo padrão ISO SQL para ambos %e mod(). Alterar essa funcionalidade agora resultaria em anos de código que dependem do comportamento existente para quebrar.

    Em vez disso, eu esperaria que a Microsoft documentasse claramente a funcionalidade existente para deixar claro qual método eles estão usando para calcular o módulo. Se tivermos sorte, eles podem introduzir uma nova sintaxe que executa o módulo usando o outro método também, para que as pessoas possam selecionar o comportamento apropriado para suas necessidades.

    • 2

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