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