做数据分析的同学应该都有过这种痛苦经历:数据库里存着一大堆日志、HTML、JSON 大文本,想用正则表达式匹配一下敏感信息或者提取关键字段,结果发现——
varchar(max) 不被支持,只能 LEFT(text, 8000) 截断后再跑正则。 🤦♂️
好消息来了!随着 SQL Server 2025 CU5 的发布,T-SQL 原生正则表达式功能终于实现了 全函数、全场景的 LOB(大对象)支持。所有 7 个正则函数现在都可以直接处理最大 2 MB 的 varchar(max) / nvarchar(max) 输入。
Azure SQL Database、SQL Database in Fabric 以及开启了「始终最新」更新策略的 Azure SQL Managed Instance 已经提前用上了这个能力,自建 SQL Server 的同学升级到 CU5 即可体验。
先说重点:这次到底改了什么?
在 SQL Server 2025 首次发布原生正则时,只有 3 个标量函数支持 LOB 输入:
| 函数 | 初始版本支持 LOB | CU5 后支持 LOB | |------|:-:|:-:| | REGEXP_LIKE | ✅ | ✅ | | REGEXP_COUNT | ✅ | ✅ | | REGEXP_INSTR | ✅ | ✅ | | REGEXP_REPLACE | ❌ | ✅ | | REGEXP_SUBSTR | ❌ | ✅ | | REGEXP_MATCHES (表值函数) | ❌ | ✅ | | REGEXP_SPLIT_TO_TABLE (表值函数) | ❌ | ✅ |
简单来说:以前要写一堆截断逻辑或 CLR 函数才能干的事,现在一条 SQL 搞定。
关键参数限制
- 输入字符串上限:2 MB / 次调用(注意是单次调用,不是整列)
- 正则模式上限:8,000 字节(对绝大多数正则来说绰绰有余)
- 超过 2 MB 会报错 19311(不会静默截断,这点设计得很良心)
- 底层引擎是 Google 的 RE2,线性时间复杂度,不会出现灾难性回溯(ReDoS)——处理大文本时这一点特别重要
实战演示:7 个函数处理大文本
下面用一个贴近真实业务的例子——Web 日志分析 + HTML 清洗——来逐一展示每个函数的能力。
场景准备
假设我们有两张表:一张存 Web 访问日志(单行 258 KB),另一张存 HTML 页面(单行 775 KB)。
CREATE TABLE dbo.LogEntries
(
LogId BIGINT IDENTITY(1,1) PRIMARY KEY,
Source SYSNAME NOT NULL,
IngestedAt DATETIME2(3) NOT NULL DEFAULT SYSUTCDATETIME(),
RawPayload VARCHAR(MAX) NOT NULL -- 日志大文本列
);
CREATE TABLE dbo.HtmlPages
(
PageId INT IDENTITY(1,1) PRIMARY KEY,
Url NVARCHAR(2048) NOT NULL,
Body NVARCHAR(MAX) NOT NULL -- HTML 大文本列
);
💡 小技巧:生成测试数据时,
REPLICATE()在结果超 8000 字节时会返回 NULL,必须用REPLICATE(CAST(... AS varchar(max)), n)的写法。
① REGEXP_LIKE — 筛选包含错误的日志行
最基础也最常用的过滤操作:找出所有包含 HTTP 5xx 错误响应的日志。
SELECT LogId, Source, DATALENGTH(RawPayload) AS PayloadBytes
FROM dbo.LogEntries
WHERE REGEXP_LIKE(RawPayload, '"[A-Z]+\s[^"]+\sHTTP/1\.[01]"\s5[0-9]{2}\s');
⚠️ 注意:REGEXP_LIKE 返回的是 Boolean 类型,不是 bit。可以直接用在 WHERE/CASE WHEN/IIF 中,但不能写成 = 1 或 = 0。
另外 REGEXP_LIKE 要求数据库兼容级别 >= 170,其他标量函数不受此限制。
② REGEXP_COUNT — 大规模计数
一次性统计整个大文本中 GET 请求、POST 请求和 5xx 错误的数量:
SELECT LogId, Source,
REGEXP_COUNT(RawPayload, '"GET\s') AS Gets,
REGEXP_COUNT(RawPayload, '"POST\s') AS Posts,
REGEXP_COUNT(RawPayload, '\s5[0-9]{2}\s') AS ServerErrors
FROM dbo.LogEntries;
结果示例:258 KB 的日志里找到了 1,500 次 GET 请求、1 次 POST、1 次 5xx 错误。一行 SQL,不用把数据导出来跑 Python 脚本。
③ REGEXP_INSTR — 定位错误位置
快速找到第一个 5xx 错误出现在文本的哪个字符位置:
SELECT LogId, Source,
REGEXP_INSTR(RawPayload, '\s5[0-9]{2}\s', 1, 1, 0) AS FirstErrorPos
FROM dbo.LogEntries;
返回值是从 1 开始的字符位置,没找到就返回 0。对于排查"错误到底在哪一行"的场景非常实用。
④ REGEXP_REPLACE — ⭐ 脱敏处理(最高频场景)
这应该是大家最期待的功能之一了。以前要对大文本做 PII 脱敏,得自己写分块替换逻辑;现在一个嵌套调用搞定:
-- 一次性脱敏:信用卡号 + 社保号 + 邮箱地址
SELECT LogId,
REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE(
RawPayload,
'\b[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}\b',
'****-****-****-****'), -- 信用卡号脱敏
'\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b',
'***-**-****'), -- SSN 格式脱敏
'\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b',
'[redacted-email]' -- 邮箱脱敏
) AS RedactedPayload
FROM dbo.LogEntries;
还有个实用场景——一键去掉所有 HTML 标签:
SELECT PageId,
LEN(Body) AS OriginalLen,
LEN(REGEXP_REPLACE(Body, N'<[^>]+>', N'')) AS TextOnlyLen
FROM dbo.HtmlPages;
实测:775 KB(396,062 字符)的 HTML 文档 → 一次调用 → 纯文本只剩 100,008 字符。效率拉满。
⑤ REGEXP_SUBSTR — 提取单个值
从每条日志中提取第一个 IPv4 地址:
SELECT LogId,
REGEXP_SUBSTR(RawPayload,
'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b',
1, 1, 'c', 0) AS FirstIp
FROM dbo.LogEntries;
最后一个参数传捕获组编号的话,可以只返回特定分组的内容。
⑥ REGEXP_MATCHES — 🔥 表值函数,批量提取所有匹配
这是本次更新中生产力提升最大的函数。它可以把几 MB 的无结构文本中的每一个匹配项都提取成独立的行:
-- 提取日志中所有的邮箱地址
SELECT l.LogId, m.match_id, m.match_value AS EmailFound
FROM dbo.LogEntries AS l
CROSS APPLY REGEXP_MATCHES(
l.RawPayload,
'\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b'
) AS m
ORDER BY l.LogId, m.match_id;
更强大的是结合捕获组解析日志行——把每行日志拆成 IP、用户名、状态码、字节数等列:
-- 用捕获组解析类 Apache 日志格式
SELECT l.LogId, m.match_id,
JSON_VALUE(m.substring_matches, '$[0].value') AS Ip,
JSON_VALUE(m.substring_matches, '$[1].value') AS UserName,
JSON_VALUE(m.substring_matches, '$[2].value') AS Status,
JSON_VALUE(m.substring_matches, '$[3].value') AS Bytes
FROM dbo.LogEntries AS l
CROSS APPLY REGEXP_MATCHES(
l.RawPayload,
'^([0-9.]+)\s-\s(\S+)\s[[^\]]+]\s"[^"]+"\s([0-9]{3})\s([0-9]+)',
'm' -- 多行模式:^ 和 $ 匹配每行开头结尾
) AS m;
💡 关键点:别忘了 'm' 多行模式标志!没有它,^ 只匹配整个 250KB 文本的开头,你只能拿到第一行的结果。
REGEXP_MATCHES 返回的结果集包含以下列:
| 列名 | 类型 | 说明 | |------|------|------| | match_id | bigint | 匹配序号(从 1 开始) | | start_position | int | 匹配起始位置(1-based) | | end_position | int | 匹配结束位置 | | match_value | 同输入类型 | 整个匹配到的子串 | | substring_matches | json | 每个捕获组的详情(JSON 数组) |
⑦ REGEXP_SPLIT_TO_TABLE — 把大文本炸成行
将整个日志 payload 按换行符拆分成逐行的表格:
SELECT l.LogId, s.ordinal AS [LineNo], s.value AS LineText
FROM dbo.LogEntries AS l
CROSS APPLY REGEXP_SPLIT_TO_TABLE(l.RawPayload, '\r?\n') AS s
WHERE l.LogId = 1 AND s.value <> ''
ORDER BY s.ordinal;
拆完之后你可以随便 Join 维度表、聚合统计、写入临时表——全部集合化操作,不需要导出数据。
🔧 组合技提示:可以用 CROSS APPLY 把多个正则函数串联起来——先 SPLIT_TO_TABLE 拆行 → 再 REGEXP_MATCHES 提取字段 → 最后聚合汇总,全部在一个查询计划里完成。
关于 2MB 限制的处理建议
如果实际业务中确实有超过 2MB 的单条数据需要正则处理,微软的建议是:在数据写入时就做好拆分,比如一行存一条日志而不是把整个日志文件塞进一列。
原因是:所有 7 个函数共享同一个 2MB 上限,在查询时做分片并不现实;而在 ETL 入库阶段按逻辑单元拆分,既保证每次正则调用都在限制内,又避免了查询时的额外开销。
测量 UTF-8 字节数的正确方式:
SELECT DATALENGTH(
CONVERT(varchar(max),
Body COLLATE Latin1_General_100_CI_AS_SC_UTF8)
) AS Utf8Bytes
FROM dbo.HtmlPages;
超过 2,097,152 字节的值传入正则函数会触发 19311 错误。
如果你确实有超过 2MB 的合理场景,可以去 Azure SQL 反馈门户 提需求,产品团队会根据反馈优先级考虑调整限制。
国内用户特别关注的问题
❓ 自建 SQL Server 什么时候能用?
升级到 SQL Server 2025 CU5 即可。如果你的企业还在用 SQL Server 2019 或更早版本,需要先规划升级路径。
❓ Azure 中国区(Mooncake)支持情况如何?
Azure SQL Database 和 Managed Instance(开启 Always-up-to-date 策略)已支持。Managed Instance 正在各区域陆续 rollout 中。
❓ 和阿里云 RDS、腾讯云 CDB 相比怎么样?
目前阿里云 RDS SQL Server 和腾讯云 CDB 基于 SQL Server 企业版内核,原生正则是 SQL Server 2025 才引入的新功能,国内云厂商跟进需要时间。如果你在做技术选型,Azure SQL 在正则处理方面目前处于领先地位。
❓ 兼容级别有什么要求?
- REGEXP_LIKE 和两个 TVF(REGEXP_MATCHES / REGEXP_SPLIT_TO_TABLE):需要兼容级别 170
- 其他标量函数(COUNT / INSTR / REPLACE / SUBSTR):所有兼容级别都可用
总结
| 对比项 | CU5 之前 | CU5 之后(及当前 Azure SQL) | |--------|---------|--------------------------| | 支持的函数数 | 3 个标量函数 | 全部 7 个(含 2 个 TVF) | | 最大输入大小 | 8000 字节(非 LOB) | 2 MB(LOB) | | 脱敏处理 | 需自定义分块逻辑 | 一条 REGEXP_REPLACE 搞定 | | 日志解析 | 导出后用外部工具 | 数据库内 REGEXP_MATCHES 直接干 | | HTML 清洗 | 应用层处理 | 单次函数调用完成 |
一句话总结:以后别再把数据导出去 grep 了,数据库里就能干,而且是用 RE2 引擎——快且安全。 🧠
📎 原文链接:https://techcommunity.microsoft.com/t5/azure-sql-blog/regex-support-for-lob-types-in-t-sql-available-in-azure-sql-sql/ba-p/4522396 📅 发布日期:2026-05-22


