sql >> Databasteknik >  >> RDS >> Mysql

Hur man får en rest med MOD() i PostgreSQL, MS SQL Server och MySQL

Problem:

Du vill hitta den (icke-negativa) resten.

Exempel:

I tabellen numbers , du har två kolumner med heltal:a och 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 vill beräkna resten genom att dividera a av b . Varje rest ska vara ett icke-negativt heltalsvärde som är mindre än b .

Lösning 1 (inte helt korrekt):

SELECT
  a,
  b,
  MOD(a, b) AS remainder
FROM numbers;

Resultatet är:

a b återstoden
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 fel
0 0 fel

Diskussion:

Denna lösning fungerar korrekt om a är icke-negativ. Men när den är negativ följer den inte den matematiska definitionen av resten.

Begreppsmässigt är en rest det som finns kvar efter en heltalsdelning av a av b . Matematiskt sett är en återstod av två heltal ett icke-negativt heltal som är mindre än divisorn b . Mer exakt är det ett tal r∈{0,1,...,b - 1} för vilket det finns något heltal k så att a =k * b + r . T.ex.:

5 = 1 * 3 + 2 , så resten av 5 och 3 är lika med 2 .

9 = 3 * 3 + 0 , så resten av 9 och 3 är lika med 0 .

5 = (-1) * (-3) + 2 , så resten av 5 och -3 är lika med 2 .

Så här är MOD(a, b) fungerar för de icke-negativa utdelningarna i kolumnen a . Uppenbarligen visas ett fel om divisorn b är 0 , eftersom du inte kan dividera med 0 .

Att få rätt återstod är problematiskt när utdelningen a är ett negativt tal. Tyvärr, MOD(a, b) kan returnera ett negativt värde när a är negativt. T.ex.:

MOD(-2, 5) returnerar -2 när den ska returnera 3 .

MOD(-5, -3) returnerar -2 när den ska returnera 1 .

Lösning 2 (rätt för alla siffror):

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 är:

a b återstoden
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 fel
0 0 fel

Diskussion:

För att beräkna resten av en division mellan alla två heltal (negativa eller icke-negativa), kan du använda CASE WHEN konstruktion. När MOD(a, b) är icke-negativ, resten är helt enkelt MOD(a, b) . Annars måste vi korrigera resultatet som returneras av MOD(a, b) .

Hur får du rätt återstod när MOD() returnerar ett negativt värde? Du bör lägga till det absoluta värdet av divisorn till MOD(a, b) . Det vill säga gör det till MOD(a, b) + ABS(b) :

MOD(-2, 5) returnerar -2 när den ska returnera 3 . Du kan fixa detta genom att lägga till 5 .

MOD(-5, -3) returnerar -2 när den ska returnera 1 . Du kan fixa detta genom att lägga till 3 .

När MOD(a, b) returnerar ett negativt tal, CASE WHEN resultatet ska vara MOD(a, b) + ABS(b) . Så här får vi lösning 2. Om du behöver en uppdatering om hur ABS() funktionen fungerar, ta en titt i kokboken Hur man beräknar ett absolut värde i SQL.

Naturligtvis kan du fortfarande inte dividera något tal med 0 . Så, om b = 0 , får du ett felmeddelande.

Lösning 3 (korrekt för alla siffror):

SELECT
  a,
  b,
  MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2 AS remainder
FROM numbers;

Resultatet är:

a b återstoden
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 fel
0 0 fel

Diskussion:

Det finns ett annat sätt att lösa detta problem. Istället för ett CASE WHEN , använd en mer komplex enrads matematisk formel:

MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2

I lösning 2, MOD(a, b) + ABS(b) returnerades för fall där MOD(a, b) < 0 . Observera att MOD(a, b) + ABS(b) = MOD(a, b) + ABS(b) * 1 when MOD(a, b) < 0 .

Däremot returnerar du MOD(a, b) när MOD(a, b) >= 0 . Observera att MOD(a, b) = MOD(a, b) + ABS(b) * 0 when MOD(a, b) >= 0 .

Så vi kan multiplicera ABS(b) med ett uttryck som är lika med 1 för en negativ MOD(a, b) och 0 för en icke-negativ MOD(a, b) . Sedan MOD(a, b) är alltid ett heltal, uttrycket MOD(a, b) + 0.5 är alltid positiv för MOD(a, b) ≥ 0 och negativ för MOD(a, b) < 0 . Du kan använda vilket positivt tal som helst som är mindre än 1 istället för 0.5 .

Teckenfunktionen SIGN() returnerar 1 om dess argument är strikt positivt, -1 om den är strikt negativ, och 0 om det är lika med 0 . Du behöver dock något som endast returnerar 0 och 1 , inte 1 och -1 . Så här fixar du detta:

(1 - 1) / 2 = 0

(1 - (-1)) / 2 = 1

Sedan, det korrekta uttrycket som du multiplicerar ABS(b) med är:

(1 - SIGN(MOD(a, b) + 0.5)) / 2

Så hela formeln är:

MOD(a, b) + ABS(b) * (1 - SIGN(MOD(a, b) + 0.5)) / 2


  1. FORALL-uttalande med INDICES-OF Bound-klausul i Oracle Database

  2. JSON_MERGE_PATCH() – Utför en RFC 7396-kompatibel sammanslagning av JSON-dokument i MySQL

  3. Säkerhetskopiera en databas i SQL Server 2017

  4. Hur får man tabellskript i Oracle SQL Developer?