DECIMAL(M, D) / NUMERIC(M, D)
⭐ Essencial 💾 Variável (~4 Bytes a cada 9 dígitos). DECIMAL(10,2) ocupa 5 Bytes.Precisão exata para DINHEIRO, preços, saldos bancários, impostos e pesos comerciais.
O tipo DECIMAL(M, D) armazena números exatamente como foram digitados (em base 10), sem erros de arredondamento binário. O parâmetro M (Precisão) é o total de dígitos (antes + depois da vírgula), e o parâmetro D (Escala) é a quantidade de dígitos após a vírgula. Exemplo: `DECIMAL(10, 2)` guarda até 10 dígitos no total, sendo 8 antes da vírgula e 2 depois (até 99.999.999,99).
✅ Quando Usar (Casos Ideais)
- • Preços de produtos, valores de pedidos, descontos, fretes e saldos financeiros (`DECIMAL(10, 2)` ou `DECIMAL(12, 2)`).
- • Taxas de juros, alíquotas de impostos ou cotações de moedas/cripto (`DECIMAL(18, 8)`).
- • Pesos e medidas de venda por quilo ou litro (`DECIMAL(8, 3)` — ex: 1,250 kg).
🚫 Quando NÃO Usar (Armadilhas)
- • NUNCA troque `DECIMAL` por `FLOAT` ou `DOUBLE` para guardar dinheiro! Em ponto flutuante binário, `0.1 + 0.2` resulta em `0.30000000000000004`, gerando furos de centavos no caixa.
- • No PHP, ao ler e gravar `DECIMAL` no PDO, trafegue como `string` (`PDO::PARAM_STR`) e use a extensão `bcmath` para cálculos financeiros exatos.
📐 Como Configurar no DrawDB Abrir DrawDB ↗
DECIMAL(10, 2)
NUMERIC(10, 2) ou DECIMAL(10, 2)
NUMERIC(10, 2) (ou armazenar como INTEGER em centavos)
📋 Exemplos Reais de Colunas com DECIMAL:
CREATE TABLE itens_pedido (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
pedido_id BIGINT UNSIGNED NOT NULL,
produto_id INT UNSIGNED NOT NULL,
quantidade SMALLINT UNSIGNED NOT NULL DEFAULT 1,
preco_unitario DECIMAL(10, 2) NOT NULL,
desconto DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
subtotal DECIMAL(12, 2) GENERATED ALWAYS AS ((quantidade * preco_unitario) - desconto) STORED
);
<?php
declare(strict_types=1);
// Em PHP, trate valores monetários como string numérica com BCMath ou centavos inteiros
// para jamais perder precisão em floats!
$precoUnitario = '129.90';
$desconto = '15.00';
// Validação de formato decimal (até 8 inteiros e 2 casas decimais)
if (!preg_match('/^\d{1,8}(\.\d{1,2})?$/', $precoUnitario)) {
throw new InvalidArgumentException('Formato de preço inválido.');
}
// Cálculo exato no PHP usando BCMath (2 casas decimais)
$precoFinal = bcsub($precoUnitario, $desconto, 2); // "114.90"
// No PDO, SEMPRE envie DECIMAL como PDO::PARAM_STR para preservar a precisão exata!
$stmt = $pdo->prepare('
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario, desconto)
VALUES (:pedido_id, :produto_id, :qtd, :preco, :desconto)
');
$stmt->bindValue(':pedido_id', 5001, PDO::PARAM_INT);
$stmt->bindValue(':produto_id', 99, PDO::PARAM_INT);
$stmt->bindValue(':qtd', 2, PDO::PARAM_INT);
$stmt->bindValue(':preco', $precoUnitario, PDO::PARAM_STR);
$stmt->bindValue(':desconto', $desconto, PDO::PARAM_STR);
$stmt->execute();