Problem:
Du vil finde den (ikke-negative) rest.
Eksempel:
I tabellen numbers
, du har to kolonner med heltal:a
og 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 vil beregne resten ved at dividere a
af b
. Hver rest skal være en ikke-negativ heltalværdi, der er mindre end b
.
Løsning 1 (ikke helt korrekt):
SELECT a, b, MOD(a, b) AS remainder FROM numbers;
Resultatet er:
a | b | resten |
---|---|---|
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 | fejl |
0 | 0 | fejl |
Diskussion:
Denne løsning fungerer korrekt, hvis a er ikke-negativ. Men når den er negativ, følger den ikke den matematiske definition af resten.
Konceptuelt er en rest det, der er tilbage efter en heltalsdeling af a
af b
. Matematisk er en rest af to heltal et ikke-negativt heltal, der er mindre end divisoren b
. Mere præcist er det et tal r∈{0,1,...,b - 1} for hvilket der findes et heltal k, således at a =k * b + r . F.eks.:
5 = 1 * 3 + 2
, så resten af 5 og 3 er lig med 2
.
9 = 3 * 3 + 0
, så resten af 9 og 3 er lig med 0
.
5 = (-1) * (-3) + 2
, så resten af 5 og -3 er lig med 2
.
Sådan er MOD(a, b)
virker for de ikke-negative udbytter i kolonnen a
. Der vises naturligvis en fejl, hvis divisoren b
er 0
, fordi du ikke kan dividere med 0
.
At få den korrekte rest er problematisk, når udbyttet a er et negativt tal. Desværre, MOD(a, b)
kan returnere en negativ værdi, når a er negativ. F.eks.:
MOD(-2, 5)
returnerer -2
hvornår den skulle returnere 3
.
MOD(-5, -3)
returnerer -2
hvornår den skulle returnere 1
.
Løsning 2 (korrekt for alle tal):
SELECT a, b, CASE WHEN MOD(a, b) >= 0 THEN MOD(a, b) ELSE MOD(a, b) + ABS(b) END AS remainder FROM numbers;
Resultatet er:
a | b | resten |
---|---|---|
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 | fejl |
0 | 0 | fejl |
Diskussion:
At beregne resten af en division mellem enhver to heltal (negative eller ikke-negative), kan du bruge CASE WHEN
konstruktion. Når MOD(a, b)
er ikke-negativ, er resten blot MOD(a, b)
. Ellers skal vi rette resultatet returneret af MOD(a, b)
.
Hvordan får du den korrekte rest, når MOD()
returnerer en negativ værdi? Du skal tilføje den absolutte værdi af divisoren til MOD(a, b)
. Det vil sige, gør det til MOD(a, b) + ABS(b)
:
MOD(-2, 5)
returnerer -2
hvornår den skulle returnere 3
. Du kan rette dette ved at tilføje 5
.
MOD(-5, -3)
returnerer -2
hvornår den skulle returnere 1
. Du kan rette dette ved at tilføje 3
.
Når MOD(a, b)
returnerer et negativt tal, CASE WHEN
resultatet skal være MOD(a, b) + ABS(b)
. Sådan får vi løsning 2. Hvis du har brug for en genopfriskning af, hvordan ABS()
funktionen virker, tag et kig i kogebogen Sådan beregnes en absolut værdi i SQL.
Selvfølgelig kan du stadig ikke dividere noget tal med 0
. Så hvis b = 0
, får du en fejl.
Løsning 3 (korrekt for alle tal):
SELECT a, b, MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2 AS remainder FROM numbers;
Resultatet er:
a | b | resten |
---|---|---|
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 | fejl |
0 | 0 | fejl |
Diskussion:
Der er en anden måde at løse dette problem på. I stedet for en CASE WHEN
, brug en mere kompleks en-linje matematisk formel:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2
I løsning 2, MOD(a, b) + ABS(b)
blev returneret for tilfælde, hvor MOD(a, b) < 0
. Bemærk, at MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0
.
I modsætning hertil returnerer du MOD(a, b)
når MOD(a, b) >= 0
. Bemærk, at MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0
.
Så vi kan gange ABS(b)
ved et udtryk, der er lig med 1 for en negativ MOD(a, b)
og 0
for en ikke-negativ MOD(a, b)
. Siden MOD(a, b)
er altid et heltal, udtrykket MOD(a, b) + 0.5
er altid positiv for MOD(a, b) ≥ 0
og negativ for MOD(a, b) < 0
. Du kan bruge ethvert positivt tal mindre end 1
i stedet for 0.5
.
Tegnfunktionen SIGN()
returnerer 1
hvis argumentet er strengt positivt, -1
hvis det er strengt negativt, og 0
hvis det er lig med 0
. Du har dog brug for noget, der kun returnerer 0
og 1
, ikke 1
og -1
. Sådan løser du dette:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Derefter det korrekte udtryk, som du multiplicerer ABS(b)
med er:
(1 - SIGN(MOD(a, b) + 0.5)) / 2
Så hele formlen er:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2