Mysql
 sql >> Base de données >  >> RDS >> Mysql

Le reste dans PostgreSQL, MS SQL Server, MySQL et SQLite

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,
  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.

C'est exactement comme ça a % b fonctionne pour les dividendes non négatifs dans la colonne a :

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 .

É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, a % b peut renvoyer une valeur négative lorsque a est négatif. Ex. :

-2 % 5 renvoie -2 quand il devrait retourner 3 .

-5 % -3 renvoie -2 quand il doit retourner 1 .

Solution 2 (correcte pour tous les nombres) :

SELECT
  a,
  b,
  CASE WHEN a % b >= 0
    THEN a % b
  ELSE
    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 de any deux entiers (négatifs ou non négatifs), vous pouvez utiliser le CASE WHEN construction. Si a % b est non négatif, le reste est simplement a % b . Sinon, il faut corriger le résultat renvoyé par a % b .

Si a % b renvoie une valeur négative, vous devez ajouter la valeur absolue d'un diviseur à a % b . Autrement dit, faites-en a % b + ABS(b) :

-2 % 5 renvoie -2 quand il devrait retourner 3 . Vous pouvez résoudre ce problème en ajoutant 5 .

-5 % (-3) renvoie -2 quand il doit retourner 1 . Vous pouvez résoudre ce problème en ajoutant 3 .

Quand a % b renvoie une valeur négative, le CASE WHEN le résultat doit être a % b + ABS(b) . C'est ainsi que vous obtenez 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, si b = 0 , vous obtiendrez toujours une erreur.

Solution 3 (correcte pour tous les nombres) :

SELECT
  a,
  b,
  a % b + ABS(b) * (1 - SIGN(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 :

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

Dans la solution 2, a % b + ABS(b) a été renvoyé pour les cas où a % b < 0 . Notez que a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 .

Donc, nous pouvons multiplier ABS(b) par une expression qui vaut 1 pour les valeurs négatives de a % b et 0 pour les valeurs non négatives de a % b . Depuis a % b est toujours un entier, l'expression a % b + 0.5 est toujours positif pour a % b >= 0 et négatif pour 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 . Mais pas de soucis ! Voici comment résoudre ce problème :

(1 - 1) / 2 = 0

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

Ensuite, l'expression correcte par laquelle vous devez multiplier ABS(b) est :

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

Ainsi, la formule entière est :

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