問題:
(負ではない)剰余を見つけたい。
例:
表のnumbers 、整数の2つの列があります: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 |
aを除算して余りを計算したい bによる 。各余りは、bよりも小さい非負の整数値である必要があります 。
解決策1(完全に正しいわけではありません):
SELECT a, b, a % b AS remainder FROM numbers;
結果は次のとおりです。
| a | b | 残り |
|---|---|---|
| 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 | エラー |
| 0 | 0 | エラー |
ディスカッション:
aが負でない場合、このソリューションは正しく機能します。ただし、負の場合は、余りの数学的定義に従いません。
概念的には、剰余はaの整数除算の後に残るものです。 bによる 。数学的には、2つの整数の余りは、除数bよりも小さい非負の整数です。 。より正確には、a =k * b +rとなる整数kが存在する数r∈{0,1、...、b-1}です。
これはまさにa % b a列の負でない配当に対して機能します :
5 = 1 * 3 + 2 、したがって、5と3の余りは2に等しくなります 。
9 = 3 * 3 + 0 、したがって、9と3の余りは0に等しくなります 。
5 = (-1) * (-3) + 2 、したがって、5と-3の余りは2に等しくなります 。
明らかに、除数がbの場合、エラーが表示されます。 0です 、0で除算できないため 。
配当aの場合、正しい剰余を取得することは問題があります は負の数です。残念ながら、a % b aの場合、負の値を返す可能性があります 負です。例:
-2 % 5 -2を返します 3を返す必要がある場合 。
-5 % -3 -2を返します 1を返す必要がある場合 。
解決策2(すべての数値に対して正しい):
SELECT
a,
b,
CASE WHEN a % b >= 0
THEN a % b
ELSE
a % b + ABS(b)
END AS remainder
FROM numbers;
結果は次のとおりです。
| a | b | 残り |
|---|---|---|
| 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 | エラー |
| 0 | 0 | エラー |
ディスカッション:
任意の除算の余りを計算するには 2つの整数(負または非負)の場合、CASE WHENを使用できます 工事。 a % b の場合 は負ではなく、余りは単純にa % b 。それ以外の場合は、a % b によって返される結果を修正する必要があります 。
a % b の場合 負の値を返します。除数の絶対値をa % bに追加する必要があります 。つまり、a % b + ABS(b)にします。 :
-2 % 5 -2を返します 3を返す必要がある場合 。これを修正するには、5を追加します 。
-5 % (-3) -2を返します 1を返す必要がある場合 。これは、3を追加することで修正できます 。
a % b の場合 負の値、CASE WHENを返します 結果はa % b + ABS(b)になります 。これがソリューション2の取得方法です。ABS()の方法について復習が必要な場合 関数が機能する場合は、クックブック「SQLで絶対値を計算する方法」を参照してください。
もちろん、b = 0の場合 、引き続きエラーが発生します。
解決策3(すべての数値に対して正しい):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder FROM numbers;
結果は次のとおりです。
| a | b | 残り |
|---|---|---|
| 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 | エラー |
| 0 | 0 | エラー |
ディスカッション:
この問題を解決する別の方法があります。 CASE WHENの代わりに 、より複雑な1行の数式を使用します:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2
ソリューション2では、a % b + ABS(b) a % b < 0の場合に返されました 。 a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0 。
したがって、ABS(b)を乗算できます。 a % b の負の値に対して1に等しい式によって および0 a % b の負でない値の場合 。 a % b 以降 は常に整数であり、式a % b + 0.5 a % b >= 0の場合は常に正です a % b < 0の場合は負 。 1未満の任意の正の数を使用できます 0.5の代わりに 。
符号関数SIGN() 1を返します 引数が厳密に正の場合、-1 厳密に負の場合、0 0に等しい場合 。ただし、0のみを返すものが必要です。 および1 、1ではありません および-1 。しかし、心配はありません!これを修正する方法は次のとおりです。
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
次に、ABS(b)を乗算する正しい式 は:
(1 - SIGN(a % b + 0.5)) / 2
したがって、式全体は次のようになります。
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2