Problem:
Du vill hitta den (icke-negativa) resten.
Exempel:
I tabellen numbers , du har två kolumner med heltal:a och 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 |
Du vill beräkna resten genom att dividera a av b . Varje rest ska vara ett icke-negativt heltalsvärde som är mindre än b .
Lösning 1 (inte helt korrekt):
SELECT a, b, a % b AS remainder FROM numbers;
Resultatet är:
| a | b | återstoden |
|---|---|---|
| 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 | fel |
| 0 | 0 | fel |
Diskussion:
Denna lösning fungerar korrekt om a är icke-negativ. Men när den är negativ följer den inte den matematiska definitionen av resten.
Begreppsmässigt är en rest det som finns kvar efter en heltalsdelning av a av b . Matematiskt sett är en återstod av två heltal ett icke-negativt heltal som är mindre än divisorn b . Mer exakt är det ett tal r∈{0,1,...,b - 1} för vilket det finns något heltal k så att a =k * b + r.
Så här är a % b fungerar för de icke-negativa utdelningarna i kolumnen a :
5 = 1 * 3 + 2 , så resten av 5 och 3 är lika med 2 .
9 = 3 * 3 + 0 , så resten av 9 och 3 är lika med 0 .
5 = (-1) * (-3) + 2 , så resten av 5 och -3 är lika med 2 .
Uppenbarligen visas ett fel om divisorn b är 0 , eftersom du inte kan dividera med 0 .
Att få rätt återstod är problematiskt när utdelningen a är ett negativt tal. Tyvärr, a % b kan returnera ett negativt värde när a är negativ. T.ex.:
-2 % 5 returnerar -2 när den ska returnera 3 .
-5 % -3 returnerar -2 när den ska returnera 1 .
Lösning 2 (rätt för alla siffror):
SELECT
a,
b,
CASE WHEN a % b >= 0
THEN a % b
ELSE
a % b + ABS(b)
END AS remainder
FROM numbers;
Resultatet är:
| a | b | återstoden |
|---|---|---|
| 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 | fel |
| 0 | 0 | fel |
Diskussion:
För att beräkna resten av en division av alla två heltal (negativa eller icke-negativa), kan du använda CASE WHEN konstruktion. Om a % b är icke-negativ, resten är helt enkelt a % b . Annars måste vi korrigera resultatet som returneras med a % b .
Om a % b returnerar ett negativt värde, bör du lägga till det absoluta värdet av en divisor till a % b . Det vill säga gör det till a % b + ABS(b) :
-2 % 5 returnerar -2 när den ska returnera 3 . Du kan fixa detta genom att lägga till 5 .
-5 % (-3) returnerar -2 när den ska returnera 1 . Du kan fixa detta genom att lägga till 3 .
När a % b returnerar ett negativt värde, CASE WHEN resultatet ska vara a % b + ABS(b) . Så här får du lösning 2. Om du behöver en uppdatering om hur ABS() funktionen fungerar, ta en titt i kokboken Hur man beräknar ett absolut värde i SQL.
Naturligtvis, om b = 0 , får du fortfarande ett felmeddelande.
Lösning 3 (korrekt för alla siffror):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder FROM numbers;
Resultatet är:
| a | b | återstoden |
|---|---|---|
| 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 | fel |
| 0 | 0 | fel |
Diskussion:
Det finns ett annat sätt att lösa detta problem. Istället för ett CASE WHEN , använd en mer komplex enrads matematisk formel:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2
I lösning 2, a % b + ABS(b) returnerades för fall där a % b < 0 . Observera att a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 .
Så vi kan multiplicera ABS(b) med ett uttryck som är lika med 1 för negativa värden på a % b och 0 för icke-negativa värden på a % b . Sedan a % b är alltid ett heltal, uttrycket a % b + 0.5 är alltid positiv för a % b >= 0 och negativ för a % b < 0 . Du kan använda vilket positivt tal som helst som är mindre än 1 istället för 0.5 .
Teckenfunktionen SIGN() returnerar 1 om dess argument är strikt positivt, -1 om den är strikt negativ, och 0 om det är lika med 0 . Du behöver dock något som endast returnerar 0 och 1 , inte 1 och -1 . Men inga bekymmer! Så här fixar du detta:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Sedan, det korrekta uttrycket som du ska multiplicera ABS(b) med är:
(1 - SIGN(a % b + 0.5)) / 2
Så hela formeln är:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2