sign函数返回1、-1、0,分别表示正数、负数、零;不处理null(返回null),不计算差值,仅作数学符号判断,需先构造变化量再使用。

SQL中SIGN函数返回值的含义必须先搞清
SIGN不是业务语义函数,它只做数学符号判断:输入正数返回1,负数返回-1,零返回0。它不关心“同比”还是“环比”,也不自动计算差值——你得自己先算出变化量,再喂给SIGN。
常见错误是直接对原始指标列(比如revenue)用SIGN:SIGN(revenue),结果全是1(除非有负收入),完全没反映趋势。
正确做法是先构造变化量,例如:
SELECT month, revenue, revenue - LAG(revenue) OVER (ORDER BY month) AS diff, SIGN(revenue - LAG(revenue) OVER (ORDER BY month)) AS trend FROM sales;
处理NULL和边界值时SIGN会静默失效
LAG在首行返回NULL,导致差值为NULL,而SIGN(NULL)仍是NULL——这不是bug,是标准行为,但容易被忽略,造成第一期趋势丢失。
需要显式处理,常用方式有:
- 用
COALESCE补零:SIGN(COALESCE(revenue - LAG(revenue) OVER (ORDER BY month), 0)),但注意:补零会让首期变成0(平),可能误导 - 用
CASE区分首期:CASE WHEN ROW_NUMBER() OVER (ORDER BY month) = 1 THEN NULL ELSE SIGN(...) END - 更合理的是保留
NULL,并在应用层明确“无可比周期”
另外,若差值恰好为0.0000001(浮点误差),SIGN仍返回1;若业务定义“涨跌需超±0.5%”,就不能只靠SIGN,得前置过滤或改用区间判断。
和CASE WHEN比,SIGN适合快速分三类但难扩展
如果只要粗粒度分类(涨/跌/平),SIGN比写三段CASE WHEN revenue > prev_revenue THEN 1 ...简洁得多,执行开销也略低(纯数值运算,无分支)。
但一旦需求变复杂,SIGN就力不从心了:
- 要标出“大涨”(>+10%)、“微跌”(-1%~0%),
SIGN无法表达梯度 - 想把
0单独归为“数据异常”而非“持平”,SIGN做不到 - 某些数据库(如MySQL 5.7)对
DECIMAL列的SIGN结果类型推导不稳定,可能引发隐式转换警告
此时直接上CASE WHEN更可控,SIGN只适合作为中间步骤或兜底简写。
不同数据库对SIGN的空值和精度处理有差异
PostgreSQL和SQL Server对SIGN(NULL)都返回NULL,行为一致;但Oracle会报错,必须加NVL包裹。
精度方面:SIGN(0.0)在所有主流引擎都返回0,但SIGN(-0.0)在IEEE 754下理论上应返回-1,实际中PostgreSQL和BigQuery会返回-1,而SQLite一律归零——如果你的ETL流程跨库,且依赖符号位区分“负零”(极少见),得额外校验。
真正容易被忽略的是:当输入列为INTEGER但差值超出范围(如INT32相减溢出),PostgreSQL抛错,MySQL静默转为BIGINT,结果仍可用;但SIGN本身不报溢出,问题会藏到下游计算里。











