Mysql
 sql >> Database >  >> RDS >> Mysql

Come ottenere un resto utilizzando MOD() in PostgreSQL, MS SQL Server e MySQL

Problema:

Vuoi trovare il resto (non negativo).

Esempio:

Nella tabella numbers , hai due colonne di numeri interi: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

Vuoi calcolare i resti dalla divisione di a da b . Ogni resto deve essere un valore intero non negativo inferiore a b .

Soluzione 1 (non del tutto corretta):

SELECT
  a,
  b,
  MOD(a, b) AS remainder
FROM numbers;

Il risultato è:

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 errore
0 0 errore

Discussione:

Questa soluzione funziona correttamente se a non è negativo. Tuttavia, quando è negativo, non segue la definizione matematica del resto.

Concettualmente, un resto è ciò che rimane dopo una divisione intera di a da b . Matematicamente, un resto di due numeri interi è un numero intero non negativo minore del divisore b . Più precisamente, è un numero r∈{0,1,...,b - 1} per cui esiste un numero intero k tale che a =k * b + r . Es.:

5 = 1 * 3 + 2 , quindi il resto di 5 e 3 è uguale a 2 .

9 = 3 * 3 + 0 , quindi il resto di 9 e 3 è uguale a 0 .

5 = (-1) * (-3) + 2 , quindi il resto di 5 e -3 è uguale a 2 .

Ecco come MOD(a, b) funziona per i dividendi non negativi nella colonna a . Ovviamente viene visualizzato un errore se il divisore b è 0 , perché non puoi dividere per 0 .

Ottenere il resto corretto è problematico quando il dividendo a è un numero negativo. Sfortunatamente, MOD(a, b) può restituire un valore negativo quando a è negativo. Es.:

MOD(-2, 5) restituisce -2 quando dovrebbe restituire 3 .

MOD(-5, -3) restituisce -2 quando dovrebbe restituire 1 .

Soluzione 2 (corretta per tutti i numeri):

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

Il risultato è:

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 errore
0 0 errore

Discussione:

Per calcolare il resto di una divisione tra qualsiasi due interi (negativi o non negativi), puoi usare il CASE WHEN costruzione. Quando MOD(a, b) non è negativo, il resto è semplicemente MOD(a, b) . In caso contrario, dobbiamo correggere il risultato restituito da MOD(a, b) .

Come si ottiene il resto corretto quando MOD() restituisce un valore negativo? Dovresti aggiungere il valore assoluto del divisore a MOD(a, b) . Cioè, rendilo MOD(a, b) + ABS(b) :

MOD(-2, 5) restituisce -2 quando dovrebbe restituire 3 . Puoi risolvere il problema aggiungendo 5 .

MOD(-5, -3) restituisce -2 quando dovrebbe restituire 1 . Puoi risolvere il problema aggiungendo 3 .

Quando MOD(a, b) restituisce un numero negativo, il CASE WHEN il risultato dovrebbe essere MOD(a, b) + ABS(b) . Ecco come otteniamo la Soluzione 2. Se hai bisogno di un aggiornamento su come ABS() funziona, dai un'occhiata al ricettario Come calcolare un valore assoluto in SQL.

Ovviamente, non puoi ancora dividere alcun numero per 0 . Quindi, se b = 0 , riceverai un errore.

Soluzione 3 (corretta per tutti i numeri):

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

Il risultato è:

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 errore
0 0 errore

Discussione:

C'è un altro modo per risolvere questo problema. Invece di un CASE WHEN , usa una formula matematica di una riga più complessa:

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

Nella Soluzione 2, MOD(a, b) + ABS(b) è stato restituito per i casi in cui MOD(a, b) < 0 . Nota che MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0 .

Al contrario, restituisci MOD(a, b) quando MOD(a, b) >= 0 . Nota che MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0 .

Quindi, possiamo moltiplicare ABS(b) da un'espressione uguale a 1 per un MOD(a, b) negativo e 0 per un MOD(a, b) non negativo . Poiché MOD(a, b) è sempre un numero intero, l'espressione MOD(a, b) + 0.5 è sempre positivo per MOD(a, b) ≥ 0 e negativo per MOD(a, b) < 0 . Puoi utilizzare qualsiasi numero positivo inferiore a 1 invece di 0.5 .

La funzione segno SIGN() restituisce 1 se il suo argomento è strettamente positivo, -1 se è strettamente negativo e 0 se è uguale a 0 . Tuttavia, hai bisogno di qualcosa che restituisca solo 0 e 1 , non 1 e -1 . Ecco come risolvere questo problema:

(1 - 1) / 2 = 0

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

Quindi, l'espressione corretta per la quale moltiplichi ABS(b) è:

(1 - SIGN(MOD(a, b) + 0.5)) / 2

Quindi, l'intera formula è:

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