SQL同一SELECT查询中如何实现两个筛选条件交替生效
交替匹配SQL查询实现方案
实现逻辑
逐行状态切换的匹配逻辑无法通过普通集合级WHERE条件实现,因为普通查询无法在遍历过程中保存上一行的匹配状态,整体实现分两个核心步骤:
- 先为表数据按遍历要求生成连续递增行号:注意SQL标准中不存在可靠的"默认存储顺序",必须显式指定排序规则(可根据业务使用自增主键、入库时间、业务编码排序,保证遍历顺序符合预期)
- 逐行扫描数据并维护匹配状态:从第一行开始,初始匹配目标为@Random1,当前行命中则将下一行的匹配目标切换为另一个参数,未命中则保持当前匹配目标不变,直到遍历完所有行,最终返回所有命中的记录。
支持CTE的数据库写法(SQL Server/MySQL 8.0+/PostgreSQL)
-- 入参定义 DECLARE @Random1 INT = 56; DECLARE @Random2 INT = 91; WITH OrderedData AS ( SELECT *, -- 按业务要求的遍历顺序生成连续行号,ORDER BY可根据实际规则调整 ROW_NUMBER() OVER (ORDER BY Date_input, Code) AS rn FROM CODE_DEX ), RecursiveMatch AS ( -- 递归起点:处理第一行数据,初始匹配目标为@Random1 SELECT rn, Code, FirstNo, LastNo, Date_input, CASE WHEN FirstNo = @Random1 THEN 1 ELSE 0 END AS is_match, CASE WHEN FirstNo = @Random1 THEN @Random2 ELSE @Random1 END AS next_target FROM OrderedData WHERE rn = 1 UNION ALL -- 递归处理后续行 SELECT curr.rn, curr.Code, curr.FirstNo, curr.LastNo, curr.Date_input, CASE WHEN curr.FirstNo = prev.next_target THEN 1 ELSE 0 END AS is_match, CASE WHEN curr.FirstNo = prev.next_target THEN -- 命中则切换下一行的匹配目标 CASE WHEN prev.next_target = @Random1 THEN @Random2 ELSE @Random1 END ELSE -- 未命中则保持原有匹配目标 prev.next_target END AS next_target FROM OrderedData curr JOIN RecursiveMatch prev ON curr.rn = prev.rn + 1 ) -- 返回所有命中记录 SELECT Code, FirstNo, LastNo, Date_input FROM RecursiveMatch WHERE is_match = 1 -- SQL Server需要加下一行取消递归深度限制,其他数据库可删除 OPTION (MAXRECURSION 0);
以上代码用提供的样例数据、入参@Random1=56、@Random2=91运行,返回结果完全符合预期:
| Code | FirstNo | LastNo | Date_input |
|---|---|---|---|
| ABCD5678 | 56 | 78 | 2022/07/07 |
| ABCD9111 | 91 | 11 | 2022/07/07 |
低版本MySQL(5.x)兼容写法
不支持CTE的版本可以用用户变量实现逐行状态维护:
-- 入参定义 SET @Random1 = 56; SET @Random2 = 91; -- 初始化状态变量 SET @curr_target = @Random1; SELECT Code, FirstNo, LastNo, Date_input FROM ( SELECT *, @is_match := CASE WHEN FirstNo = @curr_target THEN 1 ELSE 0 END AS is_match, @curr_target := CASE WHEN @is_match = 1 THEN CASE WHEN @curr_target = @Random1 THEN @Random2 ELSE @Random1 END ELSE @curr_target END AS dummy FROM CODE_DEX -- 必须指定排序规则,保证遍历顺序正确 ORDER BY Date_input, Code ) t WHERE is_match = 1;
注意事项
- 所有写法中ORDER BY子句必须和要求的遍历顺序完全一致,无ORDER BY的查询返回顺序会随索引变更、数据增删发生变化,不存在稳定的"默认存储顺序"。
- 如果业务上有明确的自增主键ID,直接用ID作为排序字段即可,是最可靠的遍历顺序依据。
内容的提问来源于stack exchange,提问作者aqsakd
相关产品推荐
相关产品推荐

