MySQL如何将列中0值替换为前一行非零值?是否需递归CTE?
用最近非零值填充0值的实现方案
你需要把数据里值为0的行,替换成它之前最近的非零行的值,先看你的示例数据(左列是原始值,右列是期望结果):
from | to ------------ 11 | 11 2 | 2 32 | 32 41 | 41 5 | 5 0 | 5 0 | 5 0 | 5 0 | 5 61 | 61 0 | 61 17 | 17 0 | 17 0 | 17 8 | 8 4 | 4
核心结论:不需要递归CTE,现代数据库用窗口函数就能高效解决,递归CTE只是兼容旧版本的备选方案。
方法一:窗口函数实现(推荐)
不同数据库的语法略有差异,以下是主流数据库的写法:
PostgreSQL / SQL Server 2022+
直接用LAST_VALUE结合IGNORE NULLS,把0转为NULL后,取当前行及之前最近的非NULL值:
SELECT "from" AS original_value, LAST_VALUE(CASE WHEN "from" != 0 THEN "from" END IGNORE NULLS) OVER (ORDER BY (SELECT 1) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS filled_value FROM your_table;
注:(SELECT 1)用来按数据原有顺序排序,如果你有明确的排序字段(比如主键、时间戳),替换成对应的字段即可。
不支持IGNORE NULLS的数据库(比如SQL Server旧版本、MySQL 8.0+)
用分组技巧:先给每个非零值及其后续的0值分配同一个分组ID,再取分组内的最大值(也就是那个非零值):
WITH numbered_rows AS ( SELECT "from", -- 累计非零值的数量,作为分组ID SUM(CASE WHEN "from" != 0 THEN 1 ELSE 0 END) OVER (ORDER BY (SELECT 1)) AS group_id FROM your_table ) SELECT "from" AS original_value, MAX("from") OVER (PARTITION BY group_id) AS filled_value FROM numbered_rows;
方法二:递归CTE(仅兼容旧版本数据库)
如果你的数据库不支持窗口函数(比如MySQL 5.x),可以用递归CTE,但效率远不如窗口函数:
WITH RECURSIVE cte AS ( -- 取第一行数据作为递归起点 SELECT ROW_NUMBER() OVER () AS row_id, "from" AS original_value, "from" AS filled_value FROM your_table LIMIT 1 UNION ALL -- 逐行递归处理:当前值为0就用上一行的填充值,否则用自身值 SELECT t.row_id, t."from" AS original_value, CASE WHEN t."from" = 0 THEN c.filled_value ELSE t."from" END AS filled_value FROM ( SELECT "from", ROW_NUMBER() OVER () AS row_id FROM your_table ) t JOIN cte c ON t.row_id = c.row_id + 1 ) SELECT original_value, filled_value FROM cte ORDER BY row_id;
内容的提问来源于stack exchange,提问作者sotn
相关产品推荐
相关产品推荐

