Em vez de me intrometer na resposta de Martin, acrescentarei o restante das minhas conclusões POWER()
aqui.
Segure sua calcinha.
Preâmbulo
Primeiro, apresento a você A, a documentação do MSDN paraPOWER()
:
Sintaxe
POWER ( float_expression , y )
Argumentos
float_expression
É uma expressão do tipo float ou de um tipo que pode ser implicitamente convertido em float.
Tipos de retorno
O mesmo que float_expression
.
Você pode concluir lendo a última linha que POWER()
é o tipo de retorno FLOAT
, mas leia novamente. float_expression
é "do tipo float ou de um tipo que pode ser implicitamente convertido em float". Portanto, apesar do nome, float_expression
pode realmente ser um FLOAT
, um DECIMAL
ou um INT
. Como a saída de POWER()
é a mesma de float_expression
, também pode ser um desses tipos.
Portanto, temos uma função escalar com tipos de retorno que dependem da entrada. Poderia ser?
Observações
Apresento a você a exibição B, um teste demonstrando que POWER()
lança sua saída para diferentes tipos de dados, dependendo de sua entrada .
SELECT
POWER(10, 3) AS int
, POWER(1000000000000, 3) AS numeric0 -- one trillion
, POWER(10.0, 3) AS numeric1
, POWER(10.12305, 3) AS numeric5
, POWER(1e1, 3) AS float
INTO power_test;
EXECUTE sp_help power_test;
DROP TABLE power_test;
Os resultados relevantes são:
Column_name Type Length Prec Scale
-------------------------------------------------
int int 4 10 0
numeric0 numeric 17 38 0
numeric1 numeric 17 38 1
numeric5 numeric 17 38 5
float float 8 53 NULL
O que parece estar acontecendo é que POWER()
moldes float_expression
para o menor tipo que se encaixa-lo, não incluindo BIGINT
.
Portanto, SELECT POWER(10.0, 38);
falha com um erro de estouro porque 10.0
é convertido para o NUMERIC(38, 1)
qual não é grande o suficiente para conter o resultado de 10 38 . Isso porque 10 38 se expande para levar 39 dígitos antes do decimal, enquanto que NUMERIC(38, 1)
pode armazenar 37 dígitos antes do decimal mais um depois dele. Portanto, o valor máximo que NUMERIC(38, 1)
pode reter é 10 37 - 0,1.
Armado com esse entendimento, posso invocar outra falha de estouro da seguinte forma.
SELECT POWER(1000000000, 3); -- one billion
Um bilhão (em oposição ao trilhão de milhões do primeiro exemplo, que é lançado para NUMERIC(38, 0)
) é pequeno o suficiente para caber em um INT
. Um bilhão levantado para a terceira potência, no entanto, é grande demais INT
, daí o erro de transbordamento.
Várias outras funções exibem comportamento semelhante, em que o tipo de saída depende da entrada:
- Funções matemáticas :
POWER()
, CEILING()
, FLOOR()
, RADIANS()
, DEGREES()
, eABS()
- Funções do sistema e expressões :
NULLIF()
, ISNULL()
, COALESCE()
, IIF()
, CHOOSE()
, e CASE
expressões
- Operadores aritméticos : Ambos
SELECT 2 * @MAX_INT;
e SELECT @MAX_SMALLINT + @MAX_SMALLINT;
, por exemplo, resultar em excessos aritméticas quando as variáveis são do tipo de dados chamado.
Conclusão
Nesse caso específico, a solução é usar SELECT POWER(1e1, precision)...
. Isso funcionará para todas as precisões possíveis desde que sejam 1e1
lançadas FLOAT
, o que pode conter números ridiculamente grandes .
Como essas funções são muito comuns, é importante entender que seus resultados podem ser arredondados ou causar erros de estouro devido ao comportamento deles. Se você espera ou confia em um tipo de dados específico para sua saída, expresse explicitamente a entrada relevante conforme necessário.
Então, crianças, agora que sabem disso, podem sair e prosperar.