SQLazy:找出含 5 个以上连续字母表顺序字母的字符串
问题描述
表 table_name 只有一个字符串字段 VALUE(最大长度 50)。需要找出至少含有 5 个连续按字母表升序排列的字母的字符串。即:对每行字符串提取所有字母,按原位置顺序检查,找到最长的一段连续升序字母子序列(每个字母大于等于前一个),长度 >= 5 的行符合条件。非字母字符须排除。
源数据
VALUE |
Test |
Test1 |
Tesssst |
TTTTest |
ABCDTest |
Testuvwxyz |
期望结果
VALUE |
ABCDTest |
Testuvwxyz |
以两个符合条件的字符串为例:
ABCDTest:字母部分为 A、B、C、D、T、e、s、t。前四个 A-B-C-D 连续升序,之后 T (ASCII 84) 仍然大于 D,继续升序段变为 A-B-C-D-T(5 个字母)。但下一个字母 e (ASCII 101) 大于 T (84),升序继续,最终整个字母序列 A-B-C-D-T-e-s-t 都是升序的,最长段长度 8。实际上只需 >= 5 即达标。
Testuvwxyz:字母部分为 T、e、s、t、u、v、w、x、y、z。其中 u-v-w-x-y-z 连续 6 个字母升序排列,长度 >= 5,符合条件。
SQLazy 分步实现
核心思路:先按字符串长度展开为每字符一行,过滤掉非字母字符,再用 segment 的 not_up 条件检测升序是否中断(当前字符小于前一字符时新开一组),然后按组汇总大小,最后筛选 max 长度 >= 5 的字符串。这种按字符展开、分段检测、聚合筛选的模式,是 SQLazy 处理字符串序列问题的典型范式。
Name |
Anchor |
Statement |
T1 |
table_name |
expand 50 as Position |
T2 |
compute mid(VALUE, Position, 1) as Ch |
|
T3 |
filter (Ch isalpha) |
|
T4 |
segment Ch; not_up; partition VALUE; as GrpNo |
|
T5 |
summarize Ch count as GrpSize; group VALUE, GrpNo |
|
T6 |
summarize GrpSize max as MaxRun; group VALUE |
|
T7 |
filter (MaxRun >= 5) |
|
T8 |
derive VALUE |
下面逐一解释这些步骤。
第 1 步:按字符串最大长度展开行
expand 50 as Position
将每行字符串展开为 50 行,Position 取值 1~50。这是处理字符串的预处理动作,便于后续逐字符计算。由于字符串最大长度 50,expand 50 足以覆盖所有字符位置。

第 2 步:提取每个位置的字符
compute mid(VALUE, Position, 1) as Ch
用 mid 函数取 VALUE 中 Position 位置的 1 个字符,结果记为 Ch。超出实际长度的位置,mid 返回空。

第 3 步:过滤掉非字母字符
filter (Ch isalpha)
用 isalpha 函数过滤,只保留字母(A-Z、a-z),去掉空字符、数字、符号。

第 4 步:检测升序中断(核心)
segment Ch; not_up; partition VALUE; as GrpNo
这是最关键的一步。segment 语句按 VALUE 分区,在每个分区内检查 Ch 的序列:not_up 表示不是升序的条件——当前字符(ASCII 值)小于等于前一个字符时触发分段。每次升序中断就标记新组开始,生成组号 GrpNo。连续升序的字母归入同一组。

第 5 步:统计每组的字符数量
summarize Ch count as GrpSize; group VALUE, GrpNo
按 VALUE 和 GrpNo 分组,count 统计每组内的字母个数 GrpSize。

第 6 步:取每个字符串的最大组长度
summarize GrpSize max as MaxRun; group VALUE
按 VALUE 分组,取各组 GrpSize 的最大值,得到每个字符串中最长连续升序段长度 MaxRun。

第 7 步:筛选长度 >= 5 的字符串
filter (MaxRun >= 5)
保留 MaxRun >= 5 的行。

第 8 步:最后保留结果需要的列
编译生成 SQL
确认上述步骤后,SQLazy 编译器自动生成原生 SQL(Oracle 语法):
WITH TEMP_TABLE__2 AS (
SELECT 1 AS col_1 FROM DUAL
UNION ALL SELECT col_1 + 1 FROM TEMP_TABLE__2 WHERE col_1 < 50
), t1 AS (
SELECT table_name.VALUE, col_1
FROM table_name CROSS JOIN TEMP_TABLE__2
), t2 AS (
SELECT VALUE, Position, SUBSTR(VALUE, Position, 1) AS Ch FROM t1
), t3 AS (
SELECT VALUE, Position, Ch FROM t2 WHERE REGEXP_LIKE(Ch, '^[A-Za-z]+$')
), t5 AS (
SELECT VALUE, GrpNo, COUNT(Ch) AS GrpSize
FROM (
SELECT VALUE, Position, Ch,
SUM(CASE WHEN Ch <= col__5 THEN 1 ELSE 0 END)
OVER (PARTITION BY VALUE ORDER BY VALUE
ROWS UNBOUNDED PRECEDING) + 1 AS GrpNo
FROM (SELECT t3.*, LAG(Ch,1)
OVER (PARTITION BY VALUE ORDER BY VALUE) AS col__5 FROM t3) sub
) t4 GROUP BY VALUE, GrpNo
), t6 AS (
SELECT VALUE, MAX(GrpSize) AS MaxRun
FROM t5 GROUP BY VALUE
)
SELECT VALUE FROM t6 WHERE MaxRun >= 5
SQLazy 让你用业务语言描述逻辑,而不是用 SQL 语法写嵌套查询。这个找出连续字母表顺序字母的例子,SQLazy 用 8 个步骤清晰描述了从字符展开到分段聚合的完整流程。segment 的 not_up 条件直接表达了字母表升序中断时新开组的语义,把原本需要递归 CTE + LAG + SUM 窗口函数的复杂 SQL 简化为一句可读的声明。expand 行展开配合逐字符计算,让字符串处理像表格行操作一样直观;分步计算的模式保证了从字符提取到分组筛选的每一步都可追溯、可调试。
官方链接
SQLazy 在线体验:sqlazy.com(免费,无需注册)
SQLazy 项目仓库:github.com/SPLWare/SQLazy

英文版: https://c.esproc.com/article/1785464980089