Problém:
Chcete najít (nezáporný) zbytek.
Příklad:
V tabulce numbers , máte dva sloupce celých čísel:a a 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 |
Chcete vypočítat zbytek z dělení a od b . Každý zbytek by měl být nezáporné celé číslo menší než b .
Řešení 1 (není zcela správné):
SELECT a, b, a % b AS remainder FROM numbers;
Výsledek je:
| a | b | zbytek |
|---|---|---|
| 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 | chyba |
| 0 | 0 | chyba |
Diskuse:
Toto řešení funguje správně, pokud a není záporné. Když je však záporný, neřídí se matematickou definicí zbytku.
Koncepčně je zbytek to, co zůstane po celočíselném dělení a od b . Matematicky je zbytek dvou celých čísel nezáporné celé číslo, které je menší než dělitel b . Přesněji, je to číslo r∈{0,1,...,b - 1}, pro které existuje nějaké celé číslo k takové, že a =k * b + r.
Přesně takto a % b funguje pro nezáporné dividendy ve sloupci a :
5 = 1 * 3 + 2 , takže zbytek 5 a 3 se rovná 2 .
9 = 3 * 3 + 0 , takže zbytek 9 a 3 se rovná 0 .
5 = (-1) * (-3) + 2 , takže zbytek 5 a -3 se rovná 2 .
Je zřejmé, že pokud je dělitel b, zobrazí se chyba je 0 , protože nemůžete dělit 0 .
Získání správného zbytku je problematické, když dividenda a je záporné číslo. Bohužel a % b může vrátit zápornou hodnotu, když a je negativní. Např.:
-2 % 5 vrátí -2 kdy by měl vrátit 3 .
-5 % -3 vrátí -2 kdy by měl vrátit 1 .
Řešení 2 (správné pro všechna čísla):
SELECT
a,
b,
CASE WHEN a % b >= 0
THEN a % b
ELSE
a % b + ABS(b)
END AS remainder
FROM numbers;
Výsledek je:
| a | b | zbytek |
|---|---|---|
| 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 | chyba |
| 0 | 0 | chyba |
Diskuse:
Chcete-li vypočítat zbytek dělení jakéhokoli dvě celá čísla (záporná nebo nezáporná), můžete použít CASE WHEN konstrukce. Pokud a % b je nezáporné, zbytek je jednoduše a % b . V opačném případě musíme opravit výsledek vrácený a % b .
Pokud a % b vrátí zápornou hodnotu, měli byste přidat absolutní hodnotu dělitele do a % b . To znamená, že a % b + ABS(b) :
-2 % 5 vrátí -2 kdy by měl vrátit 3 . Můžete to opravit přidáním 5 .
-5 % (-3) vrátí -2 kdy by měl vrátit 1 . Můžete to opravit přidáním 3 .
Když a % b vrátí zápornou hodnotu, CASE WHEN výsledek by měl být a % b + ABS(b) . Takto získáte Řešení 2. Pokud si potřebujete zopakovat, jak ABS() funkce funguje, podívejte se do kuchařky Jak vypočítat absolutní hodnotu v SQL.
Samozřejmě, pokud b = 0 , stále se zobrazí chyba.
Řešení 3 (správné pro všechna čísla):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder FROM numbers;
Výsledek je:
| a | b | zbytek |
|---|---|---|
| 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 | chyba |
| 0 | 0 | chyba |
Diskuse:
Existuje jiný způsob, jak tento problém vyřešit. Místo CASE WHEN , použijte složitější jednořádkový matematický vzorec:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2
V řešení 2 a % b + ABS(b) byl vrácen pro případy, kdy a % b < 0 . Všimněte si, že a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 .
Můžeme tedy vynásobit ABS(b) výrazem, který se rovná 1 pro záporné hodnoty a % b a 0 pro nezáporné hodnoty a % b . Od a % b je vždy celé číslo, výraz a % b + 0.5 je vždy kladné pro a % b >= 0 a záporné pro a % b < 0 . Můžete použít jakékoli kladné číslo menší než 1 místo 0.5 .
Funkce znaku SIGN() vrátí 1 pokud je jeho argument striktně kladný, -1 pokud je přísně záporné, a 0 pokud se rovná 0 . Potřebujete však něco, co vrátí pouze 0 a 1 , nikoli 1 a -1 . Ale žádný strach! Zde je návod, jak to opravit:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Potom správný výraz, kterým byste měli vynásobit ABS(b) je:
(1 - SIGN(a % b + 0.5)) / 2
Takže celý vzorec je:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2