Problema:
Você quer encontrar o resto (não negativo).
Exemplo:
Na tabela
numbers , você tem duas colunas de inteiros:a e b . | a | b |
|---|---|
| 9 | 3 |
| 5 | 3 |
| 2 | 3 |
| 0 | 3 |
| -2 | 3 |
| -5 | 3 |
| -9 | 3 |
| 5 | -3 |
| -5 | -3 |
| 5 | 0 |
| 0 | 0 |
Você deseja calcular os restos da divisão de
a por b . Cada resto deve ser um valor inteiro não negativo menor que b . Solução 1 (não totalmente correta):
SELECT a, b, a % b AS remainder FROM numbers;
O resultado é:
| a | b | restante |
|---|---|---|
| 9 | 3 | 0 |
| 5 | 3 | 2 |
| 2 | 3 | 2 |
| 0 | 3 | 0 |
| -2 | 3 | -2 |
| -5 | 3 | -2 |
| -9 | 3 | 0 |
| 5 | -3 | 2 |
| -5 | -3 | -2 |
| 5 | 0 | erro |
| 0 | 0 | erro |
Discussão:
Esta solução funciona corretamente se a for não negativo. No entanto, quando é negativo, não segue a definição matemática do resto.
Conceitualmente, um resto é o que resta após uma divisão inteira de
a por b . Matematicamente, um resto de dois inteiros é um inteiro não negativo que é menor que o divisor b . Mais precisamente, é um número r∈{0,1,...,b - 1} para o qual existe algum inteiro k tal que a =k * b + r. É exatamente assim que
a % b funciona para os dividendos não negativos na coluna a :5 = 1 * 3 + 2 , então o restante de 5 e 3 é igual a 2 . 9 = 3 * 3 + 0 , então o restante de 9 e 3 é igual a 0 . 5 = (-1) * (-3) + 2 , então o restante de 5 e -3 é igual a 2 . Obviamente, um erro é mostrado se o divisor
b é 0 , porque você não pode dividir por 0 . Obter o resto correto é problemático quando o dividendo
a é um número negativo. Infelizmente, a % b pode retornar um valor negativo quando a é negativo. Por exemplo.:-2 % 5 retorna -2 quando deve retornar 3 . -5 % -3 retorna -2 quando deve retornar 1 . Solução 2 (correta para todos os números):
SELECT
a,
b,
CASE WHEN a % b >= 0
THEN a % b
ELSE
a % b + ABS(b)
END AS remainder
FROM numbers;
O resultado é:
| a | b | restante |
|---|---|---|
| 9 | 3 | 0 |
| 5 | 3 | 2 |
| 2 | 3 | 2 |
| 0 | 3 | 0 |
| -2 | 3 | 1 |
| -5 | 3 | 1 |
| -9 | 3 | 0 |
| 5 | -3 | 2 |
| -5 | -3 | 1 |
| 5 | 0 | erro |
| 0 | 0 | erro |
Discussão:
Para calcular o restante de uma divisão de qualquer dois inteiros (negativos ou não negativos), você pode usar o
CASE WHEN construção. Se a % b é não negativo, o resto é simplesmente a % b . Caso contrário, precisamos corrigir o resultado retornado por a % b . Se
a % b retorna um valor negativo, você deve adicionar o valor absoluto de um divisor a a % b . Ou seja, faça a % b + ABS(b) :-2 % 5 retorna -2 quando deve retornar 3 . Você pode corrigir isso adicionando 5 . -5 % (-3) retorna -2 quando deve retornar 1 . Você pode corrigir isso adicionando 3 . Quando
a % b retorna um valor negativo, o CASE WHEN o resultado deve ser a % b + ABS(b) . É assim que você obtém a Solução 2. Se você precisar de uma atualização sobre como o ABS() funciona, dê uma olhada no livro de receitas Como calcular um valor absoluto em SQL. Claro, se
b = 0 , você ainda receberá um erro. Solução 3 (correta para todos os números):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder FROM numbers;
O resultado é:
| a | b | restante |
|---|---|---|
| 9 | 3 | 0 |
| 5 | 3 | 2 |
| 2 | 3 | 2 |
| 0 | 3 | 0 |
| -2 | 3 | 1 |
| -5 | 3 | 1 |
| -9 | 3 | 0 |
| 5 | -3 | 2 |
| -5 | -3 | 1 |
| 5 | 0 | erro |
| 0 | 0 | erro |
Discussão:
Existe outra maneira de resolver este problema. Em vez de um
CASE WHEN , use uma fórmula matemática de uma linha mais complexa:a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 Na Solução 2,
a % b + ABS(b) foi retornado para casos em que a % b < 0 . Observe que a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 . Assim, podemos multiplicar
ABS(b) por uma expressão que é igual a 1 para valores negativos de a % b e 0 para valores não negativos de a % b . Desde a % b é sempre um inteiro, a expressão a % b + 0.5 é sempre positivo para a % b >= 0 e negativo para a % b < 0 . Você pode usar qualquer número positivo menor que 1 em vez de 0.5 . A função de sinal
SIGN() retorna 1 se seu argumento for estritamente positivo, -1 se for estritamente negativo e 0 se for igual a 0 . No entanto, você precisa de algo que retorne apenas 0 e 1 , não 1 e -1 . Mas não se preocupe! Veja como você corrige isso:(1 - 1) / 2 = 0 (1 - (-1)) / 2 = 1 Então, a expressão correta pela qual você deve multiplicar
ABS(b) é:(1 - SIGN(a % b + 0.5)) / 2 Então, a fórmula inteira é:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2