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, 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 zodat a =k * b + r.
Dit is precies hoe a % b
werkt voor de niet-negatieve dividenden in de kolom a
:
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
.
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
is een negatief getal. Helaas, a % b
kan een negatieve waarde retourneren wanneer a
is negatief. Bijv.:
-2 % 5
retourneert -2
wanneer het 3
moet retourneren .
-5 % -3
retourneert -2
wanneer het 1
moet teruggeven .
Oplossing 2 (correct voor alle getallen):
SELECT a, b, CASE WHEN a % b >= 0 THEN a % b ELSE 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 van elke . te berekenen twee gehele getallen (negatief of niet-negatief), kunt u de CASE WHEN
gebruiken bouw. Als a % b
is niet-negatief, de rest is gewoon a % b
. Anders moeten we het resultaat corrigeren dat wordt geretourneerd door a % b
.
Als a % b
een negatieve waarde retourneert, moet u de absolute waarde van een deler toevoegen aan a % b
. Dat wil zeggen, maak het a % b + ABS(b)
:
-2 % 5
retourneert -2
wanneer het 3
moet retourneren . U kunt dit oplossen door 5
. toe te voegen .
-5 % (-3)
retourneert -2
wanneer het 1
moet teruggeven . U kunt dit oplossen door 3
. toe te voegen .
Wanneer a % b
geeft een negatieve waarde terug, de CASE WHEN
resultaat moet a % b + ABS(b)
. zijn . Dit is hoe u oplossing 2 krijgt. Als u een opfriscursus nodig heeft over hoe de ABS()
functie werkt, bekijk het kookboek Hoe bereken je een absolute waarde in SQL.
Natuurlijk, als b = 0
, krijg je nog steeds een foutmelding.
Oplossing 3 (correct voor alle getallen):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(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:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2
In Oplossing 2, a % b + ABS(b)
werd geretourneerd voor gevallen waarin a % b < 0
. Merk op dat a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0
.
We kunnen dus ABS(b)
. vermenigvuldigen door een uitdrukking die gelijk is aan 1 voor negatieve waarden van a % b
en 0
voor niet-negatieve waarden van a % b
. Sinds a % b
is altijd een geheel getal, de uitdrukking a % b + 0.5
is altijd positief voor a % b >= 0
en negatief voor 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
. Maar geen zorgen! Zo los je dit op:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Vervolgens de juiste uitdrukking waarmee u ABS(b)
. moet vermenigvuldigen is:
(1 - SIGN(a % b + 0.5)) / 2
De hele formule is dus:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2