Problème :
Vous voulez trouver le reste (non négatif).
Exemple :
Dans le tableau numbers
, vous avez deux colonnes d'entiers :a
et 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 |
Vous voulez calculer les restes de la division de a
par b
. Chaque reste doit être une valeur entière non négative inférieure à b
.
Solution 1 (pas tout à fait correcte) :
SELECT a, b, MOD(a, b) AS remainder FROM numbers;
Le résultat est :
a | b | reste |
---|---|---|
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 | erreur |
0 | 0 | erreur |
Discussion :
Cette solution fonctionne correctement si a est non négatif. Cependant, lorsqu'il est négatif, il ne suit pas la définition mathématique du reste.
Conceptuellement, un reste est ce qui reste après une division entière de a
par b
. Mathématiquement, un reste de deux entiers est un entier non négatif qui est plus petit que le diviseur b
. Plus précisément, c'est un nombre r∈{0,1,...,b - 1} pour lequel il existe un entier k tel que a =k * b + r . Ex. :
5 = 1 * 3 + 2
, donc le reste de 5 et 3 est égal à 2
.
9 = 3 * 3 + 0
, donc le reste de 9 et 3 est égal à 0
.
5 = (-1) * (-3) + 2
, donc le reste de 5 et -3 est égal à 2
.
C'est ainsi que MOD(a, b)
fonctionne pour les dividendes non négatifs dans la colonne a
. Évidemment, une erreur est affichée si le diviseur b
est 0
, car vous ne pouvez pas diviser par 0
.
Obtenir le reste correct est problématique lorsque le dividende a est un nombre négatif. Malheureusement, MOD(a, b)
peut renvoyer une valeur négative lorsque a est négatif. Ex. :
MOD(-2, 5)
renvoie -2
quand il devrait retourner 3
.
MOD(-5, -3)
renvoie -2
quand il doit retourner 1
.
Solution 2 (correcte pour tous les nombres) :
SELECT a, b, CASE WHEN MOD(a, b) >= 0 THEN MOD(a, b) ELSE MOD(a, b) + ABS(b) END AS remainder FROM numbers;
Le résultat est :
a | b | reste |
---|---|---|
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 | erreur |
0 | 0 | erreur |
Discussion :
Pour calculer le reste d'une division entre tout deux entiers (négatifs ou non négatifs), vous pouvez utiliser le CASE WHEN
construction. Lorsque MOD(a, b)
est non négatif, le reste est simplement MOD(a, b)
. Sinon, il faut corriger le résultat renvoyé par MOD(a, b)
.
Comment obtenez-vous le reste correct lorsque MOD()
renvoie une valeur négative ? Vous devez ajouter la valeur absolue du diviseur à MOD(a, b)
. Autrement dit, faites-en MOD(a, b) + ABS(b)
:
MOD(-2, 5)
renvoie -2
quand il devrait retourner 3
. Vous pouvez résoudre ce problème en ajoutant 5
.
MOD(-5, -3)
renvoie -2
quand il doit retourner 1
. Vous pouvez résoudre ce problème en ajoutant 3
.
Lorsque MOD(a, b)
renvoie un nombre négatif, le CASE WHEN
le résultat doit être MOD(a, b) + ABS(b)
. C'est ainsi que nous obtenons la solution 2. Si vous avez besoin d'un rappel sur la façon dont le ABS()
fonction fonctionne, jetez un oeil au livre de recettes Comment calculer une valeur absolue en SQL.
Bien sûr, vous ne pouvez toujours pas diviser un nombre par 0
. Donc, si b = 0
, vous obtiendrez une erreur.
Solution 3 (correcte pour tous les nombres) :
SELECT a, b, MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2 AS remainder FROM numbers;
Le résultat est :
a | b | reste |
---|---|---|
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 | erreur |
0 | 0 | erreur |
Discussion :
Il existe une autre façon de résoudre ce problème. Au lieu d'un CASE WHEN
, utilisez une formule mathématique à une ligne plus complexe :
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2
Dans la solution 2, MOD(a, b) + ABS(b)
a été renvoyé pour les cas où MOD(a, b) < 0
. Notez que MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0
.
En revanche, vous retournez MOD(a, b)
quand MOD(a, b) >= 0
. Notez que MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0
.
Donc, nous pouvons multiplier ABS(b)
par une expression qui vaut 1 pour un MOD(a, b)
négatif et 0
pour un MOD(a, b)
non négatif . Depuis MOD(a, b)
est toujours un entier, l'expression MOD(a, b) + 0.5
est toujours positif pour MOD(a, b) ≥ 0
et négatif pour MOD(a, b) < 0
. Vous pouvez utiliser n'importe quel nombre positif inférieur à 1
au lieu de 0.5
.
La fonction de signe SIGN()
renvoie 1
si son argument est strictement positif, -1
s'il est strictement négatif, et 0
s'il est égal à 0
. Cependant, vous avez besoin de quelque chose qui ne renvoie que 0
et 1
, pas 1
et -1
. Voici comment résoudre ce problème :
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Ensuite, l'expression correcte par laquelle vous multipliez ABS(b)
est :
(1 - SIGN(MOD(a, b) + 0.5)) / 2
Ainsi, la formule entière est :
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2