应优先用CONCAT替代+或||拼接字符串,因其自动跳过NULL值;临时表改用CTE或表变量;动态SQL避免循环拼接,改用STRING_AGG或分批处理。

存储过程里反复拼接字符串太占内存?用 CONCAT 替代 + 或 ||
SQL Server 和 MySQL 的字符串拼接方式不同,但共性问题是:用 +(SQL Server)或 ||(PostgreSQL)做多字段拼接时,只要任一操作数为 NULL,整个结果就变 NULL,导致后续逻辑不得不加一堆 ISNULL() 或 COALESCE() 包裹——这不仅代码膨胀,还让执行计划更难优化,间接推高内存占用。
- MySQL 8.0+ 直接用
CONCAT(col1, col2, col3),自动跳过NULL值,不需额外判断 - SQL Server 推荐用
CONCAT(col1, col2, col3)而非col1 + col2 + col3,它对NULL友好且语义清晰 - PostgreSQL 没原生
CONCAT函数,但可用CONCAT_WS('', col1, col2)达到类似效果,避免写三层COALESCE
临时表塞太多中间结果?优先用 CTE 或表变量替代 #temp 表
#temp 表会走 tempdb,每次创建/插入都触发日志写入和内存分配;尤其在循环或嵌套调用中反复建删,内存抖动明显。CTE(WITH 子句)不落盘、无元数据开销;表变量(@table_var)只在内存中维护结构,且生命周期明确。
- 单次使用、结果集 DECLARE @t TABLE(...),比
#t少约 30% 内存峰值 - 需要索引或复用多次 → 还得用
#t,但务必显式DROP TABLE #t,别依赖作用域自动清理 - CTE 不是“变量”,不能多次引用同一 CTE 名(除非用
RECURSIVE),误以为能复用会导致重复执行子查询
游标遍历逐行更新太慢还吃内存?改用集合操作 + UPDATE ... FROM
用 DECLARE cur CURSOR 配合 FETCH NEXT 处理几千行,实际是把关系运算退化成迭代,SQL 引擎无法并行、缓存失效频繁,内存常被游标上下文和未提交的锁长期占用。
- SQL Server 改写为
UPDATE t SET x = src.y FROM table1 t INNER JOIN table2 src ON t.id = src.id - MySQL 用
UPDATE t1 JOIN t2 ON t1.id = t2.id SET t1.x = t2.y,避免WHERE id IN (SELECT...)这种隐式游标 - 注意:如果业务逻辑真依赖前一行状态(比如累计求和),那游标不是错,而是设计信号——该拆到应用层或用窗口函数重写
动态 SQL 拼接长字符串卡顿?提前截断、限制长度、禁用不必要的 EXEC sp_executesql 缓存
动态 SQL 的 @sql 变量一旦超 4000 字符(SQL Server),自动升格为 NVARCHAR(MAX),而 MAX 类型在排序、连接时强制走磁盘工作表,内存占用翻倍。更麻烦的是,每次 sp_executesql 都尝试查执行计划缓存——但带大量参数的动态语句几乎不命中。
- 用
LEN(@sql) > 3000做前置检查,超长时改用分批处理,而不是硬拼 - 避免在循环内反复拼接
@sql = @sql + '...',改用STRING_AGG(SQL Server 2017+)或临时表聚合 - 确认不需要参数化缓存时,直接用
EXEC(@sql),省掉sp_executesql的元数据解析开销
真正影响存储密度的,从来不是缩进空格或少写几个注释,而是每个字符串操作是否可预测、每张临时表是否存在必要、每条动态 SQL 是否留了内存退路。这些地方没想清楚,光压缩代码行数反而会让执行器更累。

















