sqlpostgresqlmysqlmath

Divisao inteira no SQL: DIV, MOD e a armadilha do cast

Como funciona a divisao inteira no PostgreSQL e no MySQL, quando usar DIV e como combina-la com MOD para obter quociente e resto juntos.

2 min de leituraReferencesql · postgresql · mysql · math · division

A divisao inteira retorna apenas o quociente e descarta a parte fracionaria. Parece trivial, mas e justamente aqui que a analise de dados quebra com mais frequencia: um cast de tipo distraido e 7 / 2 passa de 3 para 3.5.

Dividir dois inteiros no PostgreSQL

No PostgreSQL o operador / olha os tipos dos operandos. Se ambos sao inteiros, o resultado tambem e inteiro e e truncado em direcao ao zero (truncamento, nao floor):

SELECT 7 / 2;     -- 3
SELECT -7 / 2;    -- -3, not -4
SELECT 100 / 30;  -- 3

Isso e pratico para agrupar em faixas. Por exemplo, distribuimos os usuarios em faixas de 100 ids:

SELECT
  id / 100 AS bucket,
  count(*) AS users
FROM users
GROUP BY id / 100
ORDER BY bucket;

Atencao: o truncamento vai em direcao ao zero, nao para baixo. Para numeros positivos nao ha diferenca, mas -7 / 2 da -3, enquanto o floor matematico daria -4. Quando voce realmente precisa do comportamento de floor, chame floor() de forma explicita.

A armadilha do cast

O erro classico e transformar sem querer um operando em numero de ponto flutuante. Basta um unico numeric ou ::float para que a divisao fique fracionaria:

SELECT 7 / 2;            -- 3   (int / int)
SELECT 7 / 2.0;          -- 3.5 (int / numeric)
SELECT 7::float / 2;     -- 3.5
SELECT amount / 2 FROM orders;  -- amount NUMERIC -> resultado fracionario

Se amount esta declarado como NUMERIC, entao amount / 2 ja e fracionario. Para obter o numero inteiro de "metades completas", trunque o resultado de forma explicita com DIV ou floor().

A funcao DIV(a, b)

O PostgreSQL oferece a funcao div(a, b), que sempre executa a divisao inteira e retorna o quociente truncado em direcao ao zero, independentemente dos tipos dos operandos:

SELECT div(7, 2);        -- 3
SELECT div(7.9, 2.0);    -- 3   (works on numeric too)
SELECT div(-7, 2);       -- -3

E mais segura que / porque nao depende de os operandos serem inteiros. Otima para colunas de dinheiro declaradas como NUMERIC. Vamos contar quantas centenas completas cabem em cada pedido:

SELECT
  id,
  amount,
  div(amount, 100) AS full_hundreds
FROM orders;

O operador DIV do MySQL

No MySQL / sempre retorna um resultado fracionario, mesmo com dois inteiros: SELECT 7 / 2 da 3.5. Para a divisao inteira existe um operador dedicado, DIV:

-- MySQL
SELECT 7 DIV 2;     -- 3
SELECT 100 DIV 30;  -- 3
SELECT id DIV 100 AS bucket FROM users;

No ClickHouse existem as duas formas: a funcao intDiv(7, 2) e o habitual 7 / 2 para a divisao em ponto flutuante. Ou seja, a mesma consulta se comporta de forma diferente conforme o motor, algo que vale a pena lembrar ao portar codigo.

DIV junto com MOD: quociente e resto

A divisao inteira quase sempre anda junto com MOD (o resto). Juntos eles dao o quadro completo: quantas vezes inteiras o divisor cabe e o que sobra.

SELECT
  amount,
  div(amount, 100)  AS whole_hundreds,
  mod(amount, 100)  AS remainder
FROM orders;

Um truque classico e converter segundos em minutos e segundos numa unica consulta:

SELECT
  id,
  extract(epoch FROM (now() - created_at))::int AS age_seconds,
  div(extract(epoch FROM (now() - created_at))::int, 60) AS minutes,
  mod(extract(epoch FROM (now() - created_at))::int, 60) AS seconds
FROM orders;

Outro exemplo: distribuir os funcionarios em turnos de forma ciclica (round-robin) usando o resto:

SELECT
  name,
  dept,
  mod(id, 3) AS shift   -- 0, 1, 2
FROM employees
ORDER BY shift, name;

Atencao: no PostgreSQL o sinal de mod segue o dividendo, entao mod(-7, 3) retorna -1, nao 2. Se voce precisa de um resto sempre nao negativo (por exemplo, para sharding por hash), envolva-o: mod(mod(x, n) + n, n).

Lembre do essencial: no PostgreSQL / trunca apenas quando ambos os operandos sao inteiros, no MySQL voce precisa do operador DIV para isso, e a funcao div(a, b) e a forma mais portavel e previsivel de obter um quociente inteiro em ambos os motores.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador