在MySQL里,单个等号“=”是最容易被误读的运算符之一。它并不是单纯的数学等于号,而是根据所处的SQL子句和执行上下文,分别承担赋值与比较两种职责。理解这种重载机制,是写出正确SQL的前提。

SET子句中的“=”表示赋值操作
在UPDATE语句或者SET变量的上下文中,“=”的作用是赋值,而不是判断相等。比如在更新用户余额时,我们写UPDATE user SET balance = balance - 100 WHERE id = 1,这里的balance = balance - 100就是把计算后的结果写回字段,右侧表达式先求值再赋予左侧列。很多从其他编程语言转过来的开发者会本能地认为“=”是比较,但在SQL的更新语义中它明确是赋值符号。
如果在存储过程或函数里使用SET声明用户变量,同样使用“=”。例如SET @max_id = (SELECT MAX(id) FROM user),此时“=”把子查询的结果赋给变量@max_id。需要注意的是,在SELECT列表内部如果想赋值,官方更推荐使用“:=”,因为“=”在SELECT中容易被解析成比较,从而引发歧义。下面的代码展示了在更新语句中正确运用赋值等号的方式:
UPDATE product
SET price = price * 0.9,
updated_at = NOW()
WHERE category_id = 5;
上述语句把分类为5的商品打折并刷新时间,两个“=”都是赋值。若误将其理解为比较,就会以为数据库在判断price是否等于price * 0.9,这显然不合逻辑。因此,看到SET关键字后的“=”,应立刻反应为“赋给”而不是“等于”。
WHERE与ON子句中的“=”表示等值比较
当“=”出现在WHERE、ON、HAVING等条件子句时,它的含义转变为等值比较,返回布尔值(在MySQL中为真时记为1,为假时记为0)。例如SELECT * FROM order WHERE status = 'paid',这里的“=”判断每行status字段是否等于字符串paid,只有成立才进入结果集。这种比较用途和大部分语言中的“==”等价,但MySQL用单个“=”表达。
在联表查询的ON条件里,“=”也承担比较职责,用来匹配两表关联键。如下代码通过用户id关联订单与用户表,两个“=”分别比较o.user_id与u.id、u.status与常量,都属于条件判断而非赋值:
SELECT o.order_no, u.username FROM order_table o JOIN user u ON o.user_id = u.id WHERE u.status = 1;
需要特别提醒,如果在WHERE中本想做比较却误用了“:=”,虽然语法可能通过,但语义会变成赋值并返回赋值结果作为条件,极易产生全表命中或报错。因此条件子句坚持用“=”,赋值场景才用“:=”或SET。此外,在NULL比较时,“=”不会匹配NULL,必须用IS NULL,这也是比较语义下的特殊规则。
SELECT列表与子查询中的“=”特殊形态
在SELECT后面直接跟“=”时,MySQL有时把它当作比较运算符,用来判断某表达式是否等于另一表达式,并返回1或0。比如SELECT id, status = 1 AS is_active FROM user,会为每行计算status是否等于1,生成is_active列。这种写法在报表统计中很方便,但容易和赋值混淆。若确实要在SELECT中赋值变量,应写SELECT @row_cnt := @row_cnt + 1,使用“:=”以明确意图。
另一种特殊形态是行比较,当子查询返回多列时,可以用“=”做行等值判断,例如SELECT * FROM t1 WHERE (col1, col2) = (SELECT col1, col2 FROM t2 WHERE id = 10)。这里“=”比较的是两个行构造器是否完全相等,相当于多个字段同时匹配。这种语法比写多个AND更简洁,但要求两侧列数一致且类型兼容。示例代码如下:
SELECT emp_name, dept_id
FROM employee
WHERE (dept_id, salary) = (
SELECT dept_id, MAX(salary)
FROM employee
GROUP BY dept_id
LIMIT 1
);
该行比较写法在查找特定组合的记录时非常直观,但也要注意子查询必须保证只返回一行,否则会触发“子查询返回多行”的错误。总结来看,MySQL的“=”依上下文在赋值与比较间切换,配合“:=”区分变量赋值,掌握这套规则才能避免语义错位。在编写或审查SQL时,先确认“=”所处子句,再判定它是写数据还是判条件,就能准确驾驭这一基础符号。