作者:杨际宁, 爱可生 DBA 团队成员,热衷数据库技术研究,开源数据库技术爱好者 。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 1000 字,预计阅读需要 2 分钟。
在 OCP 的告警中心里,有时会出现如下告警,告警等级为严重:
告警详情截图
该告警是提示用户在 ORDER BY
子句中使用了变量赋值表达式(如 @var := expr
)。这是一个语义不明确的用法,可能导致不可预期的执行行为。
本文将从源码分析出发,结合案例解释这一问题,并给出推荐写法。
Variable assignment in order by items will cause uncertain behavior
当用户 SQL 的 ORDER BY
子句中使用了带有赋值操作的表达式(如变量赋值 @var := expr
),OceanBase 检测到,即会触发此告警。
该告警出现在 SQL 解析模块的 ObSelectResolver::resolve_order_item
函数中,摘录如下关键代码段:
if (OB_SUCC(ret) && OB_NOT_NULL(order_item.expr_) && order_item.expr_->has_flag(CNT_ASSIGN_EXPR)) {
LOG_USER_WARN(OB_ERR_DEPRECATED_SYNTAX, "Setting user variables within expressions",
"SET variable=expression, ... or SELECT expression(s) INTO variables(s)");
if (OB_NOT_NULL(session_info_) && OB_NOT_NULL(session_info_->get_cur_exec_ctx()) &&
OB_NOT_NULL(session_info_->get_cur_exec_ctx()->get_sql_ctx())) {
const ObSqlCtx *sql_ctx = session_info_->get_cur_exec_ctx()->get_sql_ctx();
LOG_ERROR("Variable assignment in order by items will cause uncertain behavior",
K(ObString(sql_ctx->sql_id_)));
}
}
ORDER BY
中的表达式;:=
赋值操作,则会设置该标志;ORDER BY
使用了赋值表达式,就发出告警;order_item.expr_
是一个包含 赋值表达式(也就是带有 :=
运算符)的情况下才会触发;ORDER BY
表达式中,并不会在 SELECT
字段列表中出现赋值时触发。在 OceanBase 的 MySQL 租户中创建一个学生成绩表 student_scores
,记录学生 ID、姓名和成绩:
-- 建表
CREATE TABLE student_scores (
student_id INT PRIMARY KEY,
student_name VARCHAR(),
score INT
);
-- 插入数据
INSERT INTO student_scores VALUES
(, 'Alice', ),
(, 'Bob', ),
(, 'Charlie', ),
(, 'Diana', );
-- 查询逻辑:将当前最高分记录到变量 @x 中,用于后续业务逻辑,比如筛选或对比
SELECT student_id, student_name, score
FROM student_scores
ORDER BY @x := score;
执行查询 SQL 既会触发告警。
ERROR issue_dba_error (ob_log.cpp:1875) [9202][T1006_L0_G0][T1006][YB420ABA3A0F-0006360014353929-0-0] [lt=13][errcode=-4388] Unexpected internal error happen, please checkout the internal errcode(errcode=0, file="ob_select_resolver.cpp", line_no=1757, info="Variable assignment in order by items will cause uncertain behavior")
如下图显示的查询结果,行是按 score
排序的,@x
按道理最终结果应该为 95(最后一行的 score),但实际得到的却是 @x = 92
。
这说明 @x
最终的值是最后一个执行 @x := score
的那个被访问的行的值,而不是结果集中最后一行的值。因此,赋值表达式在 ORDER BY
中存在行为不确定性,严重时可能导致业务逻辑错误。
SELECT @x := MAX(score) FROM student_scores;
SELECT @x;
SET @x := ;
SELECT student_id, student_name, score, @x := score AS assign_x
FROM student_scores
ORDER BY score;
SELECT @x;
ORDER BY
语句后应该避免使用带有 赋值 操作的表达式,变量赋值逻辑应独立于排序逻辑,排序用于展示,赋值用于数据提取,两者建议不要混用。
在 PostgreSQL、Oracle 以及 OceanBase 的 Oracle 租户中,这类语法会直接报错;而 MySQL 及 OceanBase 的 MySQL 租户对此语法处理相对宽松,虽然语法合法,但执行结果可能并非预期,应尽量避免。
Row 1 (score=88) -> @x := 88
Row 2 (score=90) -> @x := 90
Row 3 (score=92) -> @x := 92
Row 4 (score=95) -> [为什么没执行“赋值”?]
再次插入一条数据:
INSERT INTO student_scores VALUES (, 'Elon', );
SELECT student_id, student_name, score
FROM student_scores
ORDER BY @x := score;
SELECT @x;
结果又符合预期了?