在过去的几年中,我们在与各类企业级数据库系统的打交道中发现,尽管技术栈在不断演进,SQL Server 作为关系型数据库的基石地位依然稳固。然而,进入 2026 年,随着“Vibe Coding”(氛围编程)和 AI 辅助开发(如 Cursor, GitHub Copilot)的普及,我们编写 SQL 的思维方式发生了深刻变化——我们更倾向于编写意图明确、逻辑紧凑且高度可读的查询。在这种背景下,像 STUFF() 这样功能强大但往往被初学者忽视的函数,实际上是我们构建高效数据处理流水线的利器。
在日常的数据库开发和管理工作中,我们经常需要对字符串数据进行各种复杂的处理。比如,你可能遇到过这样的需求:需要将多行数据合并成一行、隐藏敏感信息(如身份证号或手机号的中间几位),或者是动态地生成某种格式的报表。虽然这些操作可以通过应用程序层的代码(如 C# 或 Python)来实现,但在数据库层面直接处理往往能带来更高的效率和更简洁的逻辑。
什么是 STUFF() 函数?
简单来说,INLINECODE28fe6b6b 函数是一个“删除并插入”的复合工具。它不是简单的追加或替换,而是允许我们精确控制从字符串的哪个位置开始,删除多少个字符,然后在这个位置插入新的字符序列。想象一下在编辑文档时,你选中了一段文字,按下了删除键,然后立刻输入了新的内容——这就是 INLINECODE43593bdd 在 SQL 中做的事情。
#### 语法结构
让我们首先通过标准的语法结构来认识它:
STUFF (source_string, start, length, add_string)
#### 参数深度解析
为了确保我们能够准确无误地使用它,让我们详细拆解一下这四个参数:
- source_string (源字符串): 这是我们要操作的目标。它可以是一个字符串常量,也可以是表中的列名,甚至是返回字符串的表达式。
- start (起始位置): 这是一个整数,表示操作开始的位置。请注意:在 SQL Server 中,计数是从 1 开始的,而不是像许多编程语言那样从 0 开始。如果你填入 0,SQL Server 会将其视为从第一个字符之前开始插入(相当于不删除任何字符的追加)。
- length (删除长度): 这是一个整数,表示要从
start位置开始删除多少个字符。
* 如果 INLINECODE3ffc1741 是负数,函数会返回 INLINECODEd4f37238。
* 如果 INLINECODEf04be9b1 比 INLINECODEa7cc0744 实际长度还长,SQL Server 会聪明地删除到字符串的末尾为止。
- addstring (新增字符串): 这是要插入到 INLINECODE6f9870a3 位置的新字符序列。
关键特性: 新插入的字符串长度不需要与被删除的字符串长度一致。这意味着你可以用 3 个字符替换 10 个字符,或者用 100 个字符替换 1 个字符。这种灵活性是 INLINECODE8e70363b 区别于 INLINECODE8c3ea96e 的关键所在。
进阶应用:合并多行数据
如果说上面的例子只是热身,那么接下来这个功能则是 STUFF() 在实际开发中最具价值的场景之一:行转列(合并行数据)。
在 SQL Server 中,我们经常需要将同一列的多行数据合并成一个字符串,例如将某个学生的所有课程名称合并显示在一个单元格中,用逗号分隔。为了实现这一点,我们通常会结合 INLINECODE4c06ba8b 来使用 INLINECODE0642152c。虽然 2022+ 版本引入了 INLINECODE908c3321,但在维护旧系统或处理更复杂的 XML 数据清理时,INLINECODE09eb1e68 + FOR XML 依然是组合技之王。
#### 场景描述
假设我们有一张学生课程表 StudentCourses:
CourseName
—
Math
Science
History
Math
Art我们想要得到这样的结果:每个学生一行,课程合并显示。
#### 解决方案代码
-- 创建测试数据
DECLARE @StudentCourses TABLE (StudentID INT, CourseName VARCHAR(50));
INSERT INTO @StudentCourses VALUES
(1, ‘Math‘), (1, ‘Science‘), (1, ‘History‘),
(2, ‘Math‘), (2, ‘Art‘);
-- 使用 STUFF + FOR XML PATH 进行合并
SELECT
StudentID,
STUFF(
(
-- 2. 生成带逗号的 XML 字符串,例如:",Math,Science,History"
SELECT ‘,‘ + CourseName
FROM @StudentCourses
WHERE StudentID = A.StudentID
FOR XML PATH(‘‘)
),
1,
-- 3. STUFF 函数的参数:从第1位开始,删除1个字符(即删除第一个多余的逗号)
1,
‘‘
) AS CombinedCourses
-- 1. 外层查询获取每个学生的唯一ID
FROM @StudentCourses A
GROUP BY StudentID;
深度解析:
- 内层查询: 这个查询会为每个学生生成类似 ",Math,Science,History" 的字符串。注意第一个字符是逗号。
- STUFF() 函数: 此时我们得到了 ",Math,Science"。我们不需要第一个逗号。于是我们用
STUFF(String, 1, 1, ‘‘),意思是:在第 1 个位置,删除 1 个字符(也就是那个逗号),并插入空字符串。 - 最终结果: "Math,Science,History"。
2026 视角:生产级数据清洗与合规性
随着全球数据隐私法规(如 GDPR 和中国的个人信息保护法)在 2026 年变得更加严格,数据脱敏不再是可选项,而是必须项。在数据导出到下游分析系统或展示给客服人员之前,我们必须确保敏感信息被遮盖。STUFF() 函数在这方面提供了一种基于数据库层面的高效、不可逆的脱敏手段。
#### 场景:动态身份证号脱敏
假设我们需要向客服展示用户的身份证号,但为了保护隐私,中间的几位数字必须用星号 * 替换。我们可以构建一个通用的脱敏函数,而不是仅仅针对 18 位身份证。
示例代码:
DECLARE @IDNumber VARCHAR(18) = ‘11010519900307283X‘;
-- 目标:保留前6位和后4位,中间用*替换
-- 结果应为:110105********83X
-- 我们利用 STUFF 的特性:即使删除长度超过剩余长度,它也会安全处理
SELECT
@IDNumber AS OriginalID,
-- 逻辑:从第7位开始,删除 8 位(出生日期部分),插入 8 个星号
-- 这里我们硬编码是为了演示,实际生产中可以计算长度
STUFF(@IDNumber, 7, 8, ‘********‘) AS MaskedID;
更安全的逻辑(通用型):
有时候我们不知道字符串的确切长度,或者只想显示前 3 位和后 3 位。
-- 动态脱敏逻辑:保留前N位和后M位,中间全部替换
DECLARE @Input VARCHAR(100) = ‘ThisIsASecretPassword‘;
DECLARE @Head INT = 3; -- 保留前3位
DECLARE @Tail INT = 3; -- 保留后3位
SELECT
CASE
WHEN LEN(@Input) <= @Head + @Tail THEN '***' -- 如果太短,直接全部隐藏
ELSE
-- 1. 先用 STUFF 删除中间部分(从 Head+1 开始,删除中间所有)
-- 2. 插入由星号组成的字符串(长度 = 总长度 - 头 - 尾)
STUFF(
@Input,
@Head + 1,
LEN(@Input) - @Head - @Tail,
REPLICATE('*', LEN(@Input) - @Head - @Tail)
)
END AS SecureMask;
现代 AI 辅助开发中的 STUFF:从 Vibe Coding 到生产代码
在 2026 年,我们的开发模式已经转向“意图驱动”。当我们使用 Cursor 或 GitHub Copilot 时,我们不再逐字敲击语法,而是描述我们的意图。然而,AI 生成的代码有时虽然逻辑正确,但可能不够“地道”或高效。理解 STUFF() 的深层机制,能让我们更好地审查 AI 生成的 SQL。
#### AI 生成代码的局限性
当你对 AI 说:“把这列的所有行连起来,用逗号分开”时,AI(尤其是针对旧版 SQL Server 训练的模型)通常会给出 STUFF + FOR XML PATH 的方案。作为 2026 年的工程师,我们需要知道:这是唯一解吗?
#### 我们的最佳实践:代码审查清单
在我们的团队中,当 AI 生成了包含 STUFF 的代码时,我们会检查以下几点:
- 版本兼容性检查:如果数据库是 SQL Server 2017+,我们会强制要求 AI 将 INLINECODEd1271355 重构为 INLINECODE7ed94ba2。为什么?因为
STRING_AGG在性能优化器中的表现通常更稳定,且代码可读性更高。
-- AI 可能会生成旧风格 (为了兼容性)
-- SELECT STUFF((SELECT ‘,‘ + Name FROM Table FOR XML PATH(‘‘)), 1, 1, ‘‘)
-- 我们要求的现代风格 (SQL Server 2017+)
SELECT STRING_AGG(Name, ‘,‘)
FROM Table;
- NULL 处理策略:AI 有时会忽略 INLINECODEaedb503a 参数为 NULL 的情况。如果 INLINECODE5f98d262 是 NULL,INLINECODE43164122 会直接返回 NULL。这在数据清洗中是致命的。我们通常会修改 AI 生成的代码,加上 INLINECODE11c938ea 或
COALESCE。
-- AI 的基础版本
SELECT STUFF(Column, 1, 1, @Variable)
-- 我们的健壮版本:确保即使变量为 NULL,操作也能回退到默认行为或空字符串
SELECT STUFF(Column, 1, 1, ISNULL(@Variable, ‘‘))
性能优化与常见陷阱:来自 2026 年的工程经验
在我们最近的一个云原生数据库迁移项目中,我们遇到了一个典型的性能瓶颈。这提醒我们,即使是最简单的函数,如果不加注意,也会在大规模数据集上造成灾难性的后果。
#### 1. 索引起始位的陷阱
很多习惯了 C# 或 Python 的开发者会下意识地认为索引是从 0 开始的。在 SQL Server 中,如果你传入 start = 0 期望删除第一个字符,你会失望地发现字符串并没有变短,而是变长了(因为新字符串被加到了最前面)。
- 错误做法:试图用
STUFF(str, 0, 1, ‘A‘)去替换第一个字符。 - 正确做法:使用
STUFF(str, 1, 1, ‘A‘)。
#### 2. 避免在 WHERE 子句中滥用 STUFF (SARGable 问题)
这是 2026 年 SQL 开发中最重要的准则之一:保持查询的可搜索性。
糟糕的写法:
-- 这会导致全表扫描,因为每行都要先计算 STUFF
SELECT * FROM Users
WHERE STUFF(PhoneNumber, 4, 4, ‘****‘) = ‘138****5678‘;
推荐的写法:
-- 利用索引,先过滤再转换(如果必须展示脱敏数据)
SELECT *, STUFF(PhoneNumber, 4, 4, ‘****‘) AS MaskedPhone
FROM Users
WHERE PhoneNumber LIKE ‘138%‘;
为什么这很重要? 当我们在 WHERE 子句中对列使用函数时,SQL Server 引擎无法使用该列上的标准索引,它必须逐行计算函数值,导致 CPU 飙升和 IO 等待。在当今的 Serverless 架构中,这不仅导致查询变慢,还会直接增加计算成本。
技术选型:STRING_AGG vs STUFF (2026 版)
虽然我们今天的主角是 INLINECODE176de6fe,但作为经验丰富的技术专家,我们必须客观地评价:并不是所有场景都适合用 INLINECODE97ff4a98。
- 什么时候使用
STRING_AGG?
如果你只需要简单的合并,且使用的是 SQL Server 2017 或更高版本,STRING_AGG 在代码可读性上更胜一筹。它的逻辑更符合直觉,性能在处理极大数据集时通常经过更好的优化。
SELECT StudentID, STRING_AGG(CourseName, ‘,‘) AS Courses
FROM @StudentCourses
GROUP BY StudentID;
- 什么时候必须使用
STUFF?
1. 旧版本兼容:你需要维护基于 SQL Server 2016 或更早版本的遗留系统。
2. 特殊 XML 字符处理:在早期的 INLINECODE2c0bb093 技巧中,INLINECODE6d7fcf34 负责去除多余的分隔符,这种模式在处理特殊转义字符时非常灵活。
3. 特定的“替换插入”逻辑:当你需要替换字符串中间某一部分而不是简单的追加时,INLINECODE239f494f 无能为力,只有 INLINECODEebba48ff 能做到。
总结
在这篇文章中,我们不仅学习了 STUFF() 函数的基本语法——删除指定长度并插入新字符串,还深入探讨了它在行转列合并、数据脱敏等实际业务场景中的高效应用。更重要的是,我们从 2026 年的现代工程视角,审视了性能优化、安全性合规以及技术选型的问题。
相比单纯的 INLINECODEb2b16a2a,INLINECODE267c6630 提供了基于位置的操作能力,这让我们在处理非固定模式的字符串时拥有了极大的灵活性。无论你是需要格式化输出,还是处理复杂的报表需求,掌握 STUFF() 都会让你在 SQL Server 的字符串处理中如虎添翼。
下一步建议:
下次当你遇到需要修改字符串中特定位置的字符,或者需要将一列数据通过逗号拼接时,不妨试试 STUFF(),你会发现代码不仅写起来更短,运行起来也更高效。如果你使用的是支持 AI 辅助的 IDE(如 Cursor),你可以试着提示:“帮我使用 STUFF 函数优化这段字符串拼接逻辑”,看看 AI 能如何配合你编写出更优雅的 SQL。