sign()函数返回-1、0、1三个整数,分别表示负、零、正符号;不反映数值大小,仅用于方向判断,不可用于强度分析。

SQL中SIGN()函数的返回值到底是什么
SIGN()只返回三个整数:-1(输入为负)、0(输入为零)、1(输入为正)。它不关心数值大小,只看符号。比如SIGN(-150.5) → -1,SIGN(0) → 0,SIGN(0.001) → 1。这点很关键——你不能用它判断“波动是否剧烈”,只能判断“方向是否为正/负/零”。
直接用SIGN()计算订单金额环比变化的正负状态
典型场景是分析相邻两期订单金额差值的趋势:比如本月 vs 上月、当前订单 vs 上一笔订单。重点在于先算出差值,再套SIGN():
SELECT order_id, amount, LAG(amount) OVER (ORDER BY created_at) AS prev_amount, amount - LAG(amount) OVER (ORDER BY created_at) AS diff, SIGN(amount - LAG(amount) OVER (ORDER BY created_at)) AS trend FROM orders;
注意几个易错点:
-
LAG()默认返回NULL(首行无前值),SIGN(NULL)结果仍是NULL,不是0;需要显式处理,比如加COALESCE(diff, 0) - 如果
amount本身为NULL,整个差值会变NULL,SIGN()失效;建议提前用WHERE amount IS NOT NULL过滤 - MySQL 8.0+ 和 PostgreSQL 支持
SIGN(),但 SQLite 的SIGN()不接受NULL输入,会报错
把SIGN()结果映射成可读状态标签
直接存-1/0/1对业务不友好,常配合CASE转成文字:
SELECT
order_id,
amount,
CASE SIGN(amount - LAG(amount) OVER (ORDER BY created_at))
WHEN 1 THEN '上涨'
WHEN -1 THEN '下跌'
ELSE '持平' -- 包含 NULL 和 0 两种情况,需确认是否要区分
END AS trend_label
FROM orders;
这里有个隐藏陷阱:ELSE会同时捕获SIGN(NULL)和SIGN(0),都变成“持平”。如果你需要区分“无前序数据”和“真实持平”,得拆开判断:
- 先用
LAG()是否为NULL判断是否存在对比基准 - 再对非空差值单独用
SIGN() - 否则容易把首单误标为“持平”
为什么不用SIGN()做趋势强度判断
SIGN()天生丢弃幅度信息。比如SIGN(5)和SIGN(5000)都返回1,但业务上可能要求“涨幅超10%才算上涨”。这时候必须额外加条件:
- 用
diff / prev_amount > 0.1代替单纯SIGN(diff) = 1 - 或者用
ABS(diff) > 100筛出显著波动 -
SIGN()只适合做第一层快速分类,后续精细策略得靠数值计算
真正上线时,别只盯着SIGN()返回值,多看一眼原始diff列——很多“趋势异常”其实是数据质量问题,比如金额被清零或重复导入,SIGN()只会告诉你“变了”,但不会告诉你“变错了”。











