Problem:
Chcesz znaleźć (nieujemną) resztę.
Przykład:
W tabeli numbers , masz dwie kolumny liczb całkowitych:a i 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 |
Chcesz obliczyć reszty z dzielenia a przez b . Każda reszta powinna być nieujemną liczbą całkowitą mniejszą niż b .
Rozwiązanie 1 (nie do końca poprawne):
SELECT a, b, a % b AS remainder FROM numbers;
Wynik:
| a | b | pozostałe |
|---|---|---|
| 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 | błąd |
| 0 | 0 | błąd |
Dyskusja:
To rozwiązanie działa poprawnie, jeśli a jest nieujemne. Jednak gdy jest ujemny, nie jest zgodny z matematyczną definicją reszty.
Koncepcyjnie reszta jest tym, co pozostaje po dzieleniu całkowitym a przez b . Matematycznie reszta dwóch liczb całkowitych jest nieujemną liczbą całkowitą, która jest mniejsza niż dzielnik b . Dokładniej jest to liczba r∈{0,1,...,b - 1}, dla której istnieje pewna liczba całkowita k taka, że a =k * b + r.
Dokładnie tak a % b działa dla nieujemnych dywidend w kolumnie a :
5 = 1 * 3 + 2 , więc reszta z 5 i 3 równa się 2 .
9 = 3 * 3 + 0 , więc reszta z 9 i 3 równa się 0 .
5 = (-1) * (-3) + 2 , więc reszta z 5 i -3 równa się 2 .
Oczywiście błąd jest wyświetlany, jeśli dzielnik b to 0 , ponieważ nie można dzielić przez 0 .
Uzyskanie prawidłowej reszty jest problematyczne, gdy dywidenda a jest liczbą ujemną. Niestety, a % b może zwrócić wartość ujemną, gdy a jest ujemny. Np.:
-2 % 5 zwraca -2 kiedy powinien zwrócić 3 .
-5 % -3 zwraca -2 kiedy powinien zwrócić 1 .
Rozwiązanie 2 (poprawne dla wszystkich liczb):
SELECT
a,
b,
CASE WHEN a % b >= 0
THEN a % b
ELSE
a % b + ABS(b)
END AS remainder
FROM numbers;
Wynik:
| a | b | pozostałe |
|---|---|---|
| 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 | błąd |
| 0 | 0 | błąd |
Dyskusja:
Aby obliczyć resztę z dzielenia dowolnego dwie liczby całkowite (ujemna lub nieujemna), możesz użyć CASE WHEN budowa. Jeśli a % b jest nieujemna, reszta to po prostu a % b . W przeciwnym razie musimy poprawić wynik zwrócony przez a % b .
Jeśli a % b zwraca wartość ujemną, należy dodać wartość bezwzględną dzielnika do a % b . To znaczy, zrób to a % b + ABS(b) :
-2 % 5 zwraca -2 kiedy powinien zwrócić 3 . Możesz to naprawić, dodając 5 .
-5 % (-3) zwraca -2 kiedy powinien zwrócić 1 . Możesz to naprawić, dodając 3 .
Kiedy a % b zwraca wartość ujemną, CASE WHEN wynik powinien być a % b + ABS(b) . W ten sposób otrzymujesz Rozwiązanie 2. Jeśli potrzebujesz odświeżenia na temat sposobu ABS() funkcja działa, spójrz na książkę kucharską Jak obliczyć wartość bezwzględną w SQL.
Oczywiście, jeśli b = 0 , nadal będzie się pojawiać błąd.
Rozwiązanie 3 (poprawne dla wszystkich liczb):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder FROM numbers;
Wynik:
| a | b | pozostałe |
|---|---|---|
| 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 | błąd |
| 0 | 0 | błąd |
Dyskusja:
Jest inny sposób rozwiązania tego problemu. Zamiast CASE WHEN , użyj bardziej złożonego jednowierszowego wzoru matematycznego:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2
W rozwiązaniu 2 a % b + ABS(b) został zwrócony dla przypadków, gdy a % b < 0 . Zauważ, że a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 .
Możemy więc pomnożyć ABS(b) przez wyrażenie równe 1 dla ujemnych wartości a % b i 0 dla nieujemnych wartości a % b . Od a % b jest zawsze liczbą całkowitą, wyrażenie a % b + 0.5 jest zawsze dodatnia dla a % b >= 0 i ujemny dla a % b < 0 . Możesz użyć dowolnej liczby dodatniej mniejszej niż 1 zamiast 0.5 .
Funkcja znaku SIGN() zwraca 1 jeśli jego argument jest ściśle dodatni, -1 jeśli jest ściśle ujemna, a 0 jeśli równa się 0 . Jednak potrzebujesz czegoś, co zwraca tylko 0 i 1 , a nie 1 i -1 . Ale bez obaw! Oto jak to naprawić:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Następnie poprawne wyrażenie, przez które należy pomnożyć ABS(b) jest:
(1 - SIGN(a % b + 0.5)) / 2
Tak więc cała formuła to:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2