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 足以覆盖所有字符位置。

Picture1png

第 2 步:提取每个位置的字符

compute mid(VALUE, Position, 1) as Ch

用 mid 函数取 VALUE 中 Position 位置的 1 个字符,结果记为 Ch。超出实际长度的位置,mid 返回空。

Picture2png

第 3 步:过滤掉非字母字符

filter (Ch isalpha)

用 isalpha 函数过滤,只保留字母(A-Z、a-z),去掉空字符、数字、符号。

Picture3png

第 4 步:检测升序中断(核心)

segment Ch; not_up; partition VALUE; as GrpNo

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

Picture4png

第 5 步:统计每组的字符数量

summarize Ch count as GrpSize; group VALUE, GrpNo

按 VALUE 和 GrpNo 分组,count 统计每组内的字母个数 GrpSize。

Picture5png

第 6 步:取每个字符串的最大组长度

summarize GrpSize max as MaxRun; group VALUE

按 VALUE 分组,取各组 GrpSize 的最大值,得到每个字符串中最长连续升序段长度 MaxRun。

Picture6png

第 7 步:筛选长度 >= 5 的字符串

filter (MaxRun >= 5)

保留 MaxRun >= 5 的行。

Picture7png

第 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