Problem:
Sie wollen den (nicht negativen) Rest finden.
Beispiel:
In der Tabelle numbers
, haben Sie zwei Spalten mit ganzen Zahlen:a
und 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 |
Sie möchten die Reste aus der Division von a
berechnen durch b
. Jeder Rest sollte ein nicht negativer ganzzahliger Wert kleiner als b
sein .
Lösung 1 (nicht ganz richtig):
SELECT a, b, MOD(a, b) AS remainder FROM numbers;
Das Ergebnis ist:
a | 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 | Fehler |
0 | 0 | Fehler |
Diskussion:
Diese Lösung funktioniert korrekt, wenn a nicht negativ ist. Wenn es jedoch negativ ist, folgt es nicht der mathematischen Definition des Rests.
Konzeptionell ist ein Rest das, was nach einer ganzzahligen Division von a
übrig bleibt durch b
. Mathematisch gesehen ist ein Rest von zwei ganzen Zahlen eine nicht negative ganze Zahl, die kleiner als der Divisor b
ist . Genauer gesagt ist es eine Zahl r∈{0,1,...,b - 1}, für die es eine ganze Zahl k gibt, so dass a =k * b + r . Beispiel:
5 = 1 * 3 + 2
, also ist der Rest von 5 und 3 gleich 2
.
9 = 3 * 3 + 0
, also ist der Rest von 9 und 3 gleich 0
.
5 = (-1) * (-3) + 2
, also ist der Rest von 5 und -3 gleich 2
.
So funktioniert MOD(a, b)
funktioniert für die nicht negativen Dividenden in der Spalte a
. Offensichtlich wird ein Fehler angezeigt, wenn der Divisor b
ist ist 0
, da Sie nicht durch 0
teilen können .
Den richtigen Rest zu erhalten ist problematisch, wenn der Dividende a eine negative Zahl ist. Leider MOD(a, b)
kann einen negativen Wert zurückgeben, wenn a negativ ist. Beispiel:
MOD(-2, 5)
gibt -2
zurück wenn es 3
zurückgeben sollte .
MOD(-5, -3)
gibt -2
zurück wenn es 1
zurückgeben sollte .
Lösung 2 (richtig für alle Zahlen):
SELECT a, b, CASE WHEN MOD(a, b) >= 0 THEN MOD(a, b) ELSE MOD(a, b) + ABS(b) END AS remainder FROM numbers;
Das Ergebnis ist:
a | 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 | Fehler |
0 | 0 | Fehler |
Diskussion:
Um den Rest einer Division zwischen beliebigen zu berechnen zwei Ganzzahlen (negativ oder nicht negativ), können Sie den CASE WHEN
verwenden Konstruktion. Wenn MOD(a, b)
nichtnegativ ist, ist der Rest einfach MOD(a, b)
. Andernfalls müssen wir das von MOD(a, b)
zurückgegebene Ergebnis korrigieren .
Wie bekommt man den richtigen Rest bei MOD()
gibt einen negativen Wert zurück? Sie sollten den absoluten Wert des Divisors zu MOD(a, b)
addieren . Das heißt, machen Sie daraus MOD(a, b) + ABS(b)
:
MOD(-2, 5)
gibt -2
zurück wenn es 3
zurückgeben sollte . Sie können dies beheben, indem Sie 5
hinzufügen .
MOD(-5, -3)
gibt -2
zurück wenn es 1
zurückgeben sollte . Sie können dies beheben, indem Sie 3
hinzufügen .
Wenn MOD(a, b)
gibt eine negative Zahl zurück, den CASE WHEN
Ergebnis sollte MOD(a, b) + ABS(b)
sein . So erhalten wir Lösung 2. Wenn Sie eine Auffrischung darüber benötigen, wie die ABS()
Funktion funktioniert, werfen Sie einen Blick in das Kochbuch Wie man einen absoluten Wert in SQL berechnet.
Natürlich können Sie immer noch keine Zahl durch 0
teilen . Also, wenn b = 0
, erhalten Sie eine Fehlermeldung.
Lösung 3 (richtig für alle Zahlen):
SELECT a, b, MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2 AS remainder FROM numbers;
Das Ergebnis ist:
a | 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 | Fehler |
0 | 0 | Fehler |
Diskussion:
Es gibt eine andere Möglichkeit, dieses Problem zu lösen. Anstelle eines CASE WHEN
, verwenden Sie eine komplexere einzeilige mathematische Formel:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2
In Lösung 2, MOD(a, b) + ABS(b)
wurde für Fälle zurückgegeben, in denen MOD(a, b) < 0
. Beachten Sie, dass MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0
.
Im Gegensatz dazu geben Sie MOD(a, b)
zurück wenn MOD(a, b) >= 0
. Beachten Sie, dass MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0
.
Wir können also ABS(b)
multiplizieren durch einen Ausdruck, der für ein negatives MOD(a, b)
gleich 1 ist und 0
für ein nicht negatives MOD(a, b)
. Seit MOD(a, b)
ist immer eine ganze Zahl, der Ausdruck MOD(a, b) + 0.5
ist immer positiv für MOD(a, b) ≥ 0
und negativ für MOD(a, b) < 0
. Sie können jede positive Zahl kleiner als 1
verwenden statt 0.5
.
Die Zeichenfunktion SIGN()
gibt 1
zurück wenn sein Argument streng positiv ist, -1
wenn es streng negativ ist, und 0
wenn es gleich 0
ist . Sie benötigen jedoch etwas, das nur 0
zurückgibt und 1
, nicht 1
und -1
. So beheben Sie das:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Dann der richtige Ausdruck, mit dem Sie ABS(b)
multiplizieren ist:
(1 - SIGN(MOD(a, b) + 0.5)) / 2
Die gesamte Formel lautet also:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2