白杨商业智能工作室
#技术分享#Azure SQL

T-SQL 正则终于支持大文本了!2MB 限制全函数覆盖,日志分析党狂喜

· 白杨BI工作室

做数据分析的同学应该都有过这种痛苦经历:数据库里存着一大堆日志、HTML、JSON 大文本,想用正则表达式匹配一下敏感信息或者提取关键字段,结果发现—— varchar(max) 不被支持,只能 LEFT(text, 8000) 截断后再跑正则。 🤦‍♂️

T-SQL 正则终于支持大文本了!2MB 限制全函数覆盖,日志分析党狂喜

做数据分析的同学应该都有过这种痛苦经历:数据库里存着一大堆日志、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

相关文章

微软重磅调整:Azure SQL弃用SGX加密意味着什么

微软重磅调整:Azure SQL弃用SGX加密意味着什么

微软 Azure SQL 数据库近日宣布,将正式停用基于英特尔 SGX 安全区域的 Always Encrypted 加密方案,这一变化标志着云端机密计算在数据库加密领域的一次重要技术路线调整。

#技术分享#Azure SQL
Azure SQL数据库中心性能监控来了,你的数据库终于有救了

Azure SQL数据库中心性能监控来了,你的数据库终于有救了

微软近日宣布,Azure SQL 数据库中心(Database Hub)的性能监控功能正式进入公共预览阶段,用户现可在统一门户中直接查看数据库运行指标,无需在多套工具间来回切换。

#技术分享#Azure SQL
微软Fabric数据库中心公共预览开放,数据开发迎来巨变

微软Fabric数据库中心公共预览开放,数据开发迎来巨变

数据库管理正在从被动运维走向主动洞察。微软近日宣布,Fabric 中的数据库中心(Database Hub)正式进入公共预览阶段,这项新能力将数据库运行信号转化为可执行的引导式操作,让数据资产的管理者第一次拥有了驾驶舱式的全局视野。

#技术分享#Azure SQL
SQL Server Always On终于支持多数据库MI链接了

SQL Server Always On终于支持多数据库MI链接了

微软近日宣布,Azure SQL 托管实例(MI Link)现已支持在 SQL Server 的 Always On 可用性组中同时连接多个数据库,该功能目前处于预览阶段。这意味着企业可以在混合云架构下,更灵活地实现多数据库的云端灾备与只读分流。

#技术分享#Azure SQL
Azure SQL数据库无服务器也能自动暂停?省钱大招来了

Azure SQL数据库无服务器也能自动暂停?省钱大招来了

微软近日宣布,Azure SQL 数据库的超大规模无服务器版本正式进入自动暂停与自动恢复功能的公开预览阶段。这意味着企业可以在业务低谷期让数据库自动休眠,在请求到来时秒级唤醒,从根本上重构云数据库的成本模型。

#技术分享#Azure SQL
SQL数据同步退役倒计时,你的数据库迁移方案准备好了吗

SQL数据同步退役倒计时,你的数据库迁移方案准备好了吗

微软近日宣布,Azure SQL 数据同步服务(SQL Data Sync)正式进入退役的最后阶段,该服务将不再支持新的同步组创建,现有用户需尽快迁移至替代方案。 - 停止新建同步组:用户已无法通过门户或 API 创建新的数据同步组,现有同步组仍可运行,但不再获得功能更新。

#技术分享#Azure SQL
有问题?加企业微信 1 对 1 交流 👇
➕ 添加企业微信
备注「白杨」优先通过 · 数据洞察与智能决策
企业微信
企业微信
备注「白杨」优先通过