SIGN retorna o sinal de um numero: -1 para negativos, 0 para zero e 1 para positivos. E uma funcao minuscula, mas ela transforma o "quanto" em "para que lado" e reduz pilhas de logica CASE desajeitada a uma unica chamada.
O que SIGN retorna
A assinatura e trivial: entra um numero e sai um de tres valores -1, 0, 1.
SELECT SIGN(-42) AS neg,
SIGN(0) AS zero,
SIGN(17.5) AS pos;
O tipo do resultado espelha o do argumento: numeric entra e numeric sai, double precision da double precision. Se o argumento for NULL, o resultado tambem e NULL: nao e um quarto estado, apenas a propagacao normal do nulo.
Um uso pratico e normalizar os valores dos pedidos ate a direcao do fluxo de caixa, ignorando a magnitude:
SELECT id,
amount,
SIGN(amount) AS direction
FROM orders;
Ramificar pela direcao da mudanca
Na maioria das vezes voce recorre a SIGN para classificar a diferenca entre dois valores como "subiu", "caiu" ou "igual". Vamos comparar o valor de um pedido com o pedido anterior do mesmo usuario por meio de uma funcao de janela:
SELECT id,
user_id,
amount,
SIGN(amount - LAG(amount) OVER (
PARTITION BY user_id ORDER BY created_at
)) AS trend
FROM orders;
Os tres valores se desdobram com limpeza em rotulos usando CASE. Repare que o ramo WHEN 0 captura a igualdade exata, e nao um "quase igual":
SELECT id,
CASE SIGN(amount - 100)
WHEN -1 THEN 'below target'
WHEN 0 THEN 'on target'
WHEN 1 THEN 'above target'
END AS bucket
FROM orders;
Qualquer numero se fatora como x = SIGN(x) * ABS(x). Separar a direcao da magnitude e a principal razao para manter SIGN por perto. Por exemplo, somar o que saiu como reembolsos contra o que entrou como cobrancas em uma unica passagem:
SELECT SUM(CASE WHEN SIGN(amount) = 1 THEN amount ELSE 0 END) AS charged,
SUM(CASE WHEN SIGN(amount) = -1 THEN ABS(amount) ELSE 0 END) AS refunded
FROM orders;
A mesma combinacao ajuda ao ordenar pela proximidade de um alvo: ordene por ABS (quao longe) e mantenha o sinal a parte para mostrar de que lado um salario fica em relacao a uma referencia do departamento:
SELECT name,
dept,
salary,
SIGN(salary - 60000) AS side,
ABS(salary - 60000) AS gap
FROM employees
ORDER BY gap DESC;
Estabilidade numerica e pegadinhas
SIGN em si e estavel: nunca perde precisao nem estoura, porque so entrega tres valores. O perigo se esconde no argumento.
- Zero de ponto flutuante. Em um calculo
0.0 pode chegar como um minusculo 1e-18, e ai SIGN retorna 1, nao 0. Se o ramo do zero importa, arredonde antes: SIGN(ROUND(x::numeric, 6)).
NULL escapa por todos os ramos. Em CASE SIGN(x) nem -1, nem 0, nem 1 combinam com um NULL: adicione um IS NULL explicito ou um ELSE.
- Divisao inteira.
SIGN(a / b) com inteiros divide primeiro: SIGN(3 / 4) e SIGN(0) = 0. Converta para um tipo fracionario antes de dividir.
As diferencas entre os motores sao pequenas, mas vale conhece-las:
- PostgreSQL retorna o mesmo tipo numerico do argumento.
- MySQL tem a mesma funcao
SIGN(), mas sempre retorna um inteiro -1/0/1.
- ClickHouse oferece
sign() (em minusculas) com o mesmo comportamento -1/0/1.
SELECT SIGN(3 / 4) AS wrong,
SIGN(3.0 / 4) AS right_;
Lembre da identidade x = SIGN(x) * ABS(x): ela resolve metade das perguntas de "subiu ou caiu" sem um unico CASE aninhado.
SIGNretorna o sinal de um numero:-1para negativos,0para zero e1para positivos. E uma funcao minuscula, mas ela transforma o "quanto" em "para que lado" e reduz pilhas de logicaCASEdesajeitada a uma unica chamada.O que SIGN retorna
A assinatura e trivial: entra um numero e sai um de tres valores
-1,0,1.SELECT SIGN(-42) AS neg, -- -1 SIGN(0) AS zero, -- 0 SIGN(17.5) AS pos; -- 1O tipo do resultado espelha o do argumento:
numericentra enumericsai,double precisiondadouble precision. Se o argumento forNULL, o resultado tambem eNULL: nao e um quarto estado, apenas a propagacao normal do nulo.Um uso pratico e normalizar os valores dos pedidos ate a direcao do fluxo de caixa, ignorando a magnitude:
SELECT id, amount, SIGN(amount) AS direction -- -1 refund, 0 zero, 1 charge FROM orders;Ramificar pela direcao da mudanca
Na maioria das vezes voce recorre a
SIGNpara classificar a diferenca entre dois valores como "subiu", "caiu" ou "igual". Vamos comparar o valor de um pedido com o pedido anterior do mesmo usuario por meio de uma funcao de janela:SELECT id, user_id, amount, SIGN(amount - LAG(amount) OVER ( PARTITION BY user_id ORDER BY created_at )) AS trend -- -1 down, 0 flat, 1 up FROM orders;Os tres valores se desdobram com limpeza em rotulos usando
CASE. Repare que o ramoWHEN 0captura a igualdade exata, e nao um "quase igual":SELECT id, CASE SIGN(amount - 100) WHEN -1 THEN 'below target' WHEN 0 THEN 'on target' WHEN 1 THEN 'above target' END AS bucket FROM orders;SIGN com ABS: a direcao separada da magnitude
Qualquer numero se fatora como
x = SIGN(x) * ABS(x). Separar a direcao da magnitude e a principal razao para manterSIGNpor perto. Por exemplo, somar o que saiu como reembolsos contra o que entrou como cobrancas em uma unica passagem:SELECT SUM(CASE WHEN SIGN(amount) = 1 THEN amount ELSE 0 END) AS charged, SUM(CASE WHEN SIGN(amount) = -1 THEN ABS(amount) ELSE 0 END) AS refunded FROM orders;A mesma combinacao ajuda ao ordenar pela proximidade de um alvo: ordene por
ABS(quao longe) e mantenha o sinal a parte para mostrar de que lado um salario fica em relacao a uma referencia do departamento:SELECT name, dept, salary, SIGN(salary - 60000) AS side, -- which way off ABS(salary - 60000) AS gap -- how far off FROM employees ORDER BY gap DESC;Estabilidade numerica e pegadinhas
SIGNem si e estavel: nunca perde precisao nem estoura, porque so entrega tres valores. O perigo se esconde no argumento.0.0pode chegar como um minusculo1e-18, e aiSIGNretorna1, nao0. Se o ramo do zero importa, arredonde antes:SIGN(ROUND(x::numeric, 6)).NULLescapa por todos os ramos. EmCASE SIGN(x)nem-1, nem0, nem1combinam com umNULL: adicione umIS NULLexplicito ou umELSE.SIGN(a / b)com inteiros divide primeiro:SIGN(3 / 4)eSIGN(0) = 0. Converta para um tipo fracionario antes de dividir.As diferencas entre os motores sao pequenas, mas vale conhece-las:
SIGN(), mas sempre retorna um inteiro-1/0/1.sign()(em minusculas) com o mesmo comportamento-1/0/1.-- Pitfall: integer division hides the real sign SELECT SIGN(3 / 4) AS wrong, -- 0 SIGN(3.0 / 4) AS right_; -- 1Lembre da identidade
x = SIGN(x) * ABS(x): ela resolve metade das perguntas de "subiu ou caiu" sem um unicoCASEaninhado.