sql >> Base de Datos >  >> RDS >> Mysql

El resto en PostgreSQL, MS SQL Server, MySQL y SQLite

Problema:

Quiere encontrar el resto (no negativo).

Ejemplo:

En la tabla numbers , tienes dos columnas de enteros:a y 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

Quiere calcular los restos de dividir a por b . Cada residuo debe ser un valor entero no negativo menor que b .

Solución 1 (no del todo correcta):

SELECT
  a,
  b,
  a % b AS remainder
FROM numbers;

El resultado es:

a b resto
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 error
0 0 error

Discusión:

Esta solución funciona correctamente si a no es negativo. Sin embargo, cuando es negativo, no sigue la definición matemática del resto.

Conceptualmente, un resto es lo que queda después de una división entera de a por b . Matemáticamente, un resto de dos números enteros es un número entero no negativo que es más pequeño que el divisor b . Más precisamente, es un número r∈{0,1,...,b - 1} para el cual existe algún entero k tal que a =k * b + r.

Así es exactamente como a % b funciona para los dividendos no negativos en la columna a :

5 = 1 * 3 + 2 , por lo que el resto de 5 y 3 es igual a 2 .

9 = 3 * 3 + 0 , por lo que el resto de 9 y 3 es igual a 0 .

5 = (-1) * (-3) + 2 , por lo que el resto de 5 y -3 es igual a 2 .

Obviamente, se muestra un error si el divisor b es 0 , porque no puedes dividir por 0 .

Obtener el resto correcto es problemático cuando el dividendo a es un número negativo. Desafortunadamente, a % b puede devolver un valor negativo cuando a es negativo Por ejemplo:

-2 % 5 devuelve -2 cuando debería devolver 3 .

-5 % -3 devuelve -2 cuando debería devolver 1 .

Solución 2 (correcta para todos los números):

SELECT
  a,
  b,
  CASE WHEN a % b >= 0
    THEN a % b
  ELSE
    a % b + ABS(b)
  END AS remainder
FROM numbers;

El resultado es:

a b resto
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 error
0 0 error

Discusión:

Para calcular el resto de una división de cualquier dos enteros (negativos o no negativos), puede usar el CASE WHEN construcción. Si a % b no es negativo, el resto es simplemente a % b . De lo contrario, debemos corregir el resultado devuelto por a % b .

Si a % b devuelve un valor negativo, debe agregar el valor absoluto de un divisor a a % b . Es decir, que sea a % b + ABS(b) :

-2 % 5 devuelve -2 cuando debería devolver 3 . Puedes arreglar esto agregando 5 .

-5 % (-3) devuelve -2 cuando debería devolver 1 . Puedes arreglar esto agregando 3 .

Cuando a % b devuelve un valor negativo, el CASE WHEN el resultado debería ser a % b + ABS(b) . Así es como obtiene la Solución 2. Si necesita un repaso sobre cómo ABS() funciona, eche un vistazo al libro de cocina Cómo calcular un valor absoluto en SQL.

Por supuesto, si b = 0 , seguirá recibiendo un error.

Solución 3 (correcta para todos los números):

SELECT
  a,
  b,
  a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder
FROM numbers;

El resultado es:

a b resto
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 error
0 0 error

Discusión:

Hay otra manera de resolver este problema. En lugar de un CASE WHEN , use una fórmula matemática de una línea más compleja:

a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2

En la Solución 2, a % b + ABS(b) se devolvió para los casos en que a % b < 0 . Tenga en cuenta que a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 .

Entonces, podemos multiplicar ABS(b) por una expresión que es igual a 1 para valores negativos de a % b y 0 para valores no negativos de a % b . Desde a % b es siempre un número entero, la expresión a % b + 0.5 siempre es positivo para a % b >= 0 y negativo para a % b < 0 . Puede usar cualquier número positivo menor que 1 en lugar de 0.5 .

La función de signo SIGN() devuelve 1 si su argumento es estrictamente positivo, -1 si es estrictamente negativo, y 0 si es igual a 0 . Sin embargo, necesita algo que devuelva solo 0 y 1 , no 1 y -1 . ¡Pero no te preocupes! Así es como solucionas esto:

(1 - 1) / 2 = 0

(1 - (-1)) / 2 = 1

Luego, la expresión correcta por la que debes multiplicar ABS(b) es:

(1 - SIGN(a % b + 0.5)) / 2

Entonces, la fórmula completa es:

a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2