Masalah:
Anda ingin mencari sisa (non-negatif).
Contoh:
Dalam tabel numbers
, Anda memiliki dua kolom bilangan bulat:a
dan 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 |
Anda ingin menghitung sisa dari pembagian a
oleh b
. Setiap sisa harus berupa nilai bilangan bulat non-negatif yang lebih kecil dari b
.
Solusi 1 (tidak sepenuhnya benar):
SELECT a, b, MOD(a, b) AS remainder FROM numbers;
Hasilnya adalah:
a | b | sisa |
---|---|---|
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 | kesalahan |
0 | 0 | kesalahan |
Diskusi:
Solusi ini bekerja dengan benar jika a adalah non-negatif. Namun, jika negatif, tidak mengikuti definisi matematika dari sisanya.
Secara konseptual, sisa adalah apa yang tersisa setelah pembagian bilangan bulat a
oleh b
. Secara matematis, sisa dua bilangan bulat adalah bilangan bulat tak negatif yang lebih kecil dari pembagi b
. Lebih tepatnya, itu adalah angka r∈{0,1,...,b - 1} dimana terdapat beberapa bilangan bulat k sehingga a =k * b + r . Mis.:
5 = 1 * 3 + 2
, jadi sisa 5 dan 3 sama dengan 2
.
9 = 3 * 3 + 0
, jadi sisa 9 dan 3 sama dengan 0
.
5 = (-1) * (-3) + 2
, jadi sisa 5 dan -3 sama dengan 2
.
Begini caranya MOD(a, b)
bekerja untuk dividen non-negatif di kolom a
. Jelas, kesalahan ditampilkan jika pembagi b
adalah 0
, karena Anda tidak dapat membagi dengan 0
.
Mendapatkan sisa yang benar bermasalah ketika dividen a adalah angka negatif. Sayangnya, MOD(a, b)
dapat mengembalikan nilai negatif ketika a negatif. Mis.:
MOD(-2, 5)
mengembalikan -2
kapan seharusnya mengembalikan 3
.
MOD(-5, -3)
mengembalikan -2
kapan seharusnya mengembalikan 1
.
Solusi 2 (benar untuk semua angka):
SELECT a, b, CASE WHEN MOD(a, b) >= 0 THEN MOD(a, b) ELSE MOD(a, b) + ABS(b) END AS remainder FROM numbers;
Hasilnya adalah:
a | b | sisa |
---|---|---|
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 | kesalahan |
0 | 0 | kesalahan |
Diskusi:
Untuk menghitung sisa pembagian antara apa saja dua bilangan bulat (negatif atau non-negatif), Anda dapat menggunakan CASE WHEN
konstruksi. Ketika MOD(a, b)
tidak negatif, sisanya hanya MOD(a, b)
. Jika tidak, kita harus memperbaiki hasil yang dikembalikan oleh MOD(a, b)
.
Bagaimana Anda mendapatkan sisa yang benar ketika MOD()
mengembalikan nilai negatif? Anda harus menambahkan nilai absolut dari pembagi ke MOD(a, b)
. Yaitu, buat MOD(a, b) + ABS(b)
:
MOD(-2, 5)
mengembalikan -2
kapan seharusnya mengembalikan 3
. Anda dapat memperbaikinya dengan menambahkan 5
.
MOD(-5, -3)
mengembalikan -2
kapan seharusnya mengembalikan 1
. Anda dapat memperbaikinya dengan menambahkan 3
.
Ketika MOD(a, b)
mengembalikan angka negatif, CASE WHEN
hasilnya harus MOD(a, b) + ABS(b)
. Beginilah cara kami mendapatkan Solusi 2. Jika Anda membutuhkan penyegaran tentang cara ABS()
berfungsi, lihat buku masak Cara menghitung nilai absolut dalam SQL.
Tentu saja, Anda tetap tidak dapat membagi bilangan apa pun dengan 0
. Jadi, jika b = 0
, Anda akan mendapatkan kesalahan.
Solusi 3 (benar untuk semua angka):
SELECT a, b, MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2 AS remainder FROM numbers;
Hasilnya adalah:
a | b | sisa |
---|---|---|
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 | kesalahan |
0 | 0 | kesalahan |
Diskusi:
Ada cara lain untuk mengatasi masalah ini. Alih-alih CASE WHEN
, gunakan rumus matematika satu baris yang lebih kompleks:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2
Dalam Solusi 2, MOD(a, b) + ABS(b)
dikembalikan untuk kasus ketika MOD(a, b) < 0
. Perhatikan bahwa MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0
.
Sebaliknya, Anda mengembalikan MOD(a, b)
ketika MOD(a, b) >= 0
. Perhatikan bahwa MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0
.
Jadi, kita dapat mengalikan ABS(b)
dengan ekspresi yang sama dengan 1 untuk MOD(a, b)
negatif dan 0
untuk MOD(a, b)
non-negatif . Sejak MOD(a, b)
selalu bilangan bulat, ekspresi MOD(a, b) + 0.5
selalu positif untuk MOD(a, b) ≥ 0
dan negatif untuk MOD(a, b) < 0
. Anda dapat menggunakan bilangan positif apa pun yang kurang dari 1
bukannya 0.5
.
Fungsi tanda SIGN()
mengembalikan 1
jika argumennya benar-benar positif, -1
jika benar-benar negatif, dan 0
jika sama dengan 0
. Namun, Anda memerlukan sesuatu yang hanya mengembalikan 0
dan 1
, bukan 1
dan -1
. Inilah cara Anda memperbaikinya:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Kemudian, ekspresi yang benar untuk mengalikan ABS(b)
adalah:
(1 - SIGN(MOD(a, b) + 0.5)) / 2
Jadi, seluruh rumusnya adalah:
MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2