Probleem:
U wilt de (niet-negatieve) rest vinden.
Voorbeeld:
In de tabel numbers
, je hebt twee kolommen met gehele getallen:a
en b
.
een | b |
---|---|
9 | 3 |
5 | 3 |
2 | 3 |
0 | 3 |
-2 | 3 |
-5 | 3 |
-9 | 3 |
5 | -3 |
-5 | -3 |
5 | 0 |
0 | 0 |
U wilt de resten berekenen van het delen van a
door b
. Elke rest moet een niet-negatief geheel getal zijn dat kleiner is dan b
.
Oplossing 1 (niet helemaal correct):
SELECT a, b, MOD(a, b) AS remainder FROM numbers;
Het resultaat is:
een | b | rest |
---|---|---|
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 | fout |
0 | 0 | fout |
Discussie:
Deze oplossing werkt correct als a niet-negatief is. Als het echter negatief is, volgt het niet de wiskundige definitie van de rest.
Conceptueel is een rest wat overblijft na een geheeltallige deling van a
door b
. Wiskundig gezien is een rest van twee gehele getallen een niet-negatief geheel getal dat kleiner is dan de deler b
. Om precies te zijn, het is een getal r∈{0,1,...,b - 1} waarvoor een geheel getal k bestaat zodanig dat a =k * b + r . Bijv.:
5 = 1 * 3 + 2
, dus de rest van 5 en 3 is gelijk aan 2
.
9 = 3 * 3 + 0
, dus de rest van 9 en 3 is gelijk aan 0
.
5 = (-1) * (-3) + 2
, dus de rest van 5 en -3 is gelijk aan 2
.
Dit is hoe MOD(a, b)
werkt voor de niet-negatieve dividenden in de kolom a
. Uiteraard wordt er een fout getoond als de deler b
is 0
, omdat je niet kunt delen door 0
.
Het verkrijgen van de juiste rest is problematisch wanneer het deeltal a een negatief getal is. Helaas, MOD(a, b)
kan een negatieve waarde retourneren als a negatief is. Bijv.:
MOD(-2, 5)
retourneert -2
wanneer het 3
moet retourneren .
MOD(-5, -3)
retourneert -2
wanneer het 1
moet teruggeven .
Oplossing 2 (correct voor alle getallen):
SELECT a, b, CASE WHEN MOD(a, b) >= 0 THEN MOD(a, b) ELSE MOD(a, b) + ABS(b) END AS remainder FROM numbers;
Het resultaat is:
een | b | rest |
---|---|---|
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 | fout |
0 | 0 | fout |
Discussie:
Om de rest van een deling tussen elke . te berekenen twee gehele getallen (negatief of niet-negatief), kunt u de CASE WHEN
gebruiken bouw. Wanneer MOD(a, b)
is niet-negatief, de rest is gewoon MOD(a, b)
. Anders moeten we het resultaat corrigeren dat wordt geretourneerd door MOD(a, b)
.
Hoe krijg je de juiste rest als MOD()
geeft een negatieve waarde terug? Je moet de absolute waarde van de deler toevoegen aan MOD(a, b)
. Dat wil zeggen, maak het MOD(a, b) + ABS(b)
:
MOD(-2, 5)
retourneert -2
wanneer het 3
moet retourneren . U kunt dit oplossen door 5
. toe te voegen .
MOD(-5, -3)
retourneert -2
wanneer het 1
moet teruggeven . U kunt dit oplossen door 3
. toe te voegen .
Wanneer MOD(a, b)
geeft een negatief getal terug, de CASE WHEN
resultaat moet MOD(a, b) + ABS(b)
. zijn . Dit is hoe we oplossing 2 krijgen. Als je een opfriscursus nodig hebt over hoe de ABS()
functie werkt, bekijk het kookboek Hoe bereken je een absolute waarde in SQL.
Natuurlijk kun je nog steeds geen enkel getal delen door 0
. Dus, als b = 0
, krijg je een foutmelding.
Oplossing 3 (correct voor alle getallen):
SELECT a, b, MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2 AS remainder FROM numbers;
Het resultaat is:
een | b | rest |
---|---|---|
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 | fout |
0 | 0 | fout |
Discussie:
Er is een andere manier om dit probleem op te lossen. In plaats van een CASE WHEN
, gebruik een complexere wiskundige formule van één regel:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2
In Oplossing 2, MOD(a, b) + ABS(b)
werd geretourneerd voor gevallen waarin MOD(a, b) < 0
. Merk op dat MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0
.
Daarentegen retourneert u MOD(a, b)
wanneer MOD(a, b) >= 0
. Merk op dat MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0
.
We kunnen dus ABS(b)
. vermenigvuldigen door een uitdrukking die gelijk is aan 1 voor een negatieve MOD(a, b)
en 0
voor een niet-negatieve MOD(a, b)
. Sinds MOD(a, b)
is altijd een geheel getal, de uitdrukking MOD(a, b) + 0.5
is altijd positief voor MOD(a, b) ≥ 0
en negatief voor MOD(a, b) < 0
. U kunt elk positief getal kleiner dan 1
. gebruiken in plaats van 0.5
.
De tekenfunctie SIGN()
retourneert 1
als het argument strikt positief is, -1
als het strikt negatief is, en 0
als het gelijk is aan 0
. U hebt echter iets nodig dat alleen 0
. teruggeeft en 1
, niet 1
en -1
. Zo los je dit op:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Dan de juiste uitdrukking waarmee je ABS(b)
. vermenigvuldigt is:
(1 - SIGN(MOD(a, b) + 0.5)) / 2
De hele formule is dus:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2