SQL编程大赛第5名周仟荣:用 PostgreSQL 位运算解决数独问题,碾压常规解法!

2025 NineData 第三届数据库编程大赛圆满举办!本次大赛由 NineData 和云数据库技术社区主办,并联合佰晟智算、达梦数据、 ITPUB、优快云、IFclub、开源中国、DataFun、墨天轮等技术社区共同举办。本届大赛延续 "一条 SQL" 的核心挑战,设置题目为「用一条 SQL」解数独问题。查看赛题详情

以下是本次决赛第 5 名选手,周仟荣的参赛介绍:

第5名

周仟荣

图片

参赛选手:周仟荣

选手简介:数据分析师,来自东风日产数据服务有限公司

参赛数据库:PostgreSQL

性能评测:1 万级数据代码性能评测 15.933 秒

综合得分:82.3

以下是周仟荣选手的代码说明思路简介:


算法流程概览

  • 数组高效构建棋盘

  • 位掩码进行高效数据压缩和运算

  • 分流策略:

    1.简单题:具有唯一候选,快速扫描,不回溯

    2.剩余难题:MRV策略进行剪枝

  • 合并输出

  • FILTER、DISTINCT ON 等PostgreSQL特有语法的应用对性能有一定提升

  • 延迟物化、内存压缩(Smallint)等优化对性能有提升

图片

核心算法解析-位掩码

清洗、坐标构建

使用正则进行格式清洗,将待填空格替换为0

使用数组进行棋盘坐标构建

--示例生成中间表
create  table dwd_sudoku_step1 as
WITH RECURSIVE
-- 1. 数据源 & 强壮解析 (Robust Parsing)
src AS (
    SELECT
        id,
        puzzle,
        -- 【关键修复】:
        -- 1. regexp_replace: 先暴力剔除所有非数字、非点、非问号的字符 (包括空格、换行、制表符等)
        -- 2. translate: 再将 '.' 和 '?' 统一转为 '0'
        -- 3. 结果: 得到纯净的 81 位数字字符串,绝对不会错位
        string_to_array(
            translate(
                regexp_replace(puzzle, '[^0-9.?]', '', 'g'),
                '.?', '00'
            ),
            NULL
        )::smallint[] AS board
    FROM sudoku9_9 --题面
)
-- 2. 棋盘单元格坐标构建
    SELECT
        id, val, i,   --id,原值,81位字符串序号
        (i-1)/9 as r,   --行
        (i-1)%9 as c,   --列
        ((i-1)/27)*3 + ((i-1)%9)/3 as bx  --宫
    FROM src, unnest(board) WITH ORDINALITY as t(val, i)  --数组展开为行;
where id=1;

图片

图片

位图运算-1-二进制独热位掩码

数字1~9位掩码:

利用 二进制独热位掩码 (Binary One-hot Bitmask) 技术,将棋盘 1-9 的十进制数值映射为特定的二进制位状态。

图片

示例,第1行的已有数字 1、2、5、6、7 进行OR (|) 运算转为 二进制掩码 001110011再转为10进制 115

--以第一行为例
--棋盘数字转为二进制掩码
SELECT
    a.id           AS "ID",            --id
    a.r + 1        AS "Row",          -- 行号
    a.val         AS "Digit",         -- 数独数字
    (1 << (a.val-1))          AS "Decimal", -- 十进制位状态
    (1 << (a.val-1))::bit(9)  AS "Binary"   -- 二进制位状态
FROM dwd_sudoku_step1 a
WHERE a.id=1 AND a.val>0 AND a.r=0;
--掩码 OR (|) 运算聚合
SELECT
    a.id           AS "ID",            --id
    a.r + 1        AS "Row",          -- 行号
    array_agg(a.val)        AS "Digit",         -- 数独数字
    bit_or(1 << (a.val-1))          AS "Decimal", -- 十进制位状态
    bit_or(1 << (a.val-1))::bit(9)  AS "Binary"   -- 二进制位状态
FROM dwd_sudoku_step1 a
WHERE a.id=1 AND a.val>0 AND a.r=0
group by 1,2;

图片

位图运算-2-行、列、宫已有数字掩码(黑名单)

以上示例拓展到行、列、宫,使用位掩码 bit_or (按位或) ,将每一行、列、宫中已有的数字记录在一个二进制数中,极大提升运算性能;

--Smallint更轻量计算速度更快

-- 3. 批量初始化掩码 (Filter 聚合)
masks_calc AS (----使用“位掩码”技术。通过位运算(Bitwise OR)将每一行、列、宫中已有的数字记录在一个二进制数中
    SELECT id, array_agg(COALESCE(m, 0)::smallint ORDER BY idx) as ms
    FROM (
        SELECT id, r+1 as idx, bit_or(1 << (val-1)) FILTER (WHERE val>0) as m FROM cells GROUP BY id, r
        UNION ALL
        SELECT id, c+10 as idx, bit_or(1 << (val-1)) FILTER (WHERE val>0) as m FROM cells GROUP BY id, c
        UNION ALL
        SELECT id, bx+19 as idx, bit_or(1 << (val-1)) FILTER (WHERE val>0) as m FROM cells GROUP BY id, bx
    ) t
    GROUP BY id
),

结果为长度9*3=27的数组,分别表示行、列、宫各9组已有数字的掩码

图片

位图运算-3-核心算法 — 位掩码与集合论 (Bitwise Set Theory)

Candidate=All(511)∩Not(Used)

即:

511& (Row∣Col∣Box)

1.聚合 (OR): 用 OR (|) 操作,把行、列、宫里用过的数字聚合,得到一个‘黑名单’。

2.取反 (NOT): 黑名单翻转为白名单,原来的黑名单变成了 0,而空闲的位置变成了 1。

但这里有个问题,计算机的整数是 32 位的,取反后高位全是 1(垃圾数据)。

3.过滤 (AND): 使用 511 (9个1) 筛除高位垃圾,锁定有效候选,只保留我们关心的 1-9 位。

4.以上只进行了3次计算即完成了一个格子的填充

以id=1为例,行、列、宫已有数字掩码聚合如下

图片

以取第1行第2列、第4列空格为例

已有数字掩码分别为 (115|505|507) 和 (115|370|371),这两个空格可填数字计算如下

-- 遍历提取 (Iteration)
-- 【核心特性】全量解析,结果完整
-- 【适用场景】调试展示、回溯法分支生成 (需要所有可能性)
WITH test_calc AS (
    SELECT (511 & ~ (115|505|507)) as cand_m
    union all
    SELECT (511 & ~ (115|370|371)) as cand_m
)
SELECT
    cand_m,
    cand_m::bit(9) cand_m_bit,
    (
        SELECT string_agg(num::text, ',')
        FROM generate_series(1, 9) num
        WHERE (cand_m & (1 << (num - 1))) > 0 -- 挨个检查1-9是否在掩码里
    ) as candidates_list,
    bit_count(cand_m::bit(9)) bit_count   --掩码为1的数量
FROM test_calc;

图片

即快速求解出两个格子可填数字分别为3 和 3、4、8

注意:第一组仅有一个值,这是大部分简单题普遍情况,对于这种情况可以简化如下,只取首个值快速计算,避免消耗过多计算资源进行循环碰撞

-- 单值提取 (Bit Hack),适用于只有唯一值时快速提取
-- 【核心特性】极速计算,无循环
-- 【适用场景】贪心阶段 (WHERE bit_count=1),确保只有唯一解时使用
WITH test_calc AS (
    SELECT (511 & ~ (115|505|507)) as cand_m
    union all
    SELECT (511 & ~ (115|370|371)) as cand_m
)
select
    a.cand_m,                          --十进制表达
    a.cand_m::bit(9)  as cand_m_bit,   --二进制表达
    -- 【关键逻辑】利用 x & -x 提取最低位的 1 (Lowbit)
    -- 如果有多个候选数,这里会丢失高位数据,只返回最小的那个数字,需要限制仅存在唯一候选情况下使用,即掩码为1的数量=1
    (bit_count(((a.cand_m & -a.cand_m) - 1)::bit(32)) + 1)::smallint candidates_list,  --可选数字
    bit_count(cand_m::bit(9)) bit_count
from test_calc a;

图片

适用范围:仅存在唯一候选情况下使用,即掩码为1的数量=1

where bit_count(cand_m::bit(9))=1

图片

完整流程构建

待填格子位置索引

    ---待填格子位置索引:筛选出所有值为 0 的格子索引,打包为一个数组
empty_calc AS (
    SELECT id, array_agg(i::smallint ORDER BY i) as emp
    FROM cells WHERE val = 0 GROUP BY id
),

图片

WHERE* val = 0:

•含义:这是过滤器。在数独逻辑中,0 代表空位(即原题中的 ? 或 .)。

•作用:只关心那些“需要填”的格子,忽略那些题目自带的已知数字(1-9)。

•i::smallint:

•含义:i 是格子在 81 个字符长字符串中的绝对位置(1-81)。

•作用:强制转换为 smallint(2字节整数)。内存优化的一部分,比默认的 int(4字节)省一半内存。

•ORDER BY i:

•含义:在打包数组时,按照位置先后排序。

•作用:保证任务列表的顺序是确定性的(从左上角到右下角)。这对于后续递归逻辑的稳定性非常重要。

•array_agg**(...) as emp**:

•含义:聚合函数。将多行数据“捏”成一行里的一个数组。

•结果:emp (Empty Cells) 字段。

递归前数据准备

-- 4. 组装初始状态--递归前数据准备
prep AS (
    SELECT
        s.id, s.board,  --[零件1] 原始棋盘
        -- [零件2] 约束掩码 (27个整数)
        -- 这里包含了 9行+9列+9宫 的初始状态
        COALESCE(m.ms, '{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}'::smallint[]) as ms,
        -- [零件3] 待办空位数组 (递归的任务列表)
        COALESCE(e.emp, '{}'::smallint[]) as emp
    FROM src s
     -- 将三个独立的数据流合并为一行
    LEFT JOIN masks_calc m ON s.id = m.id
    LEFT JOIN empty_calc e ON s.id = e.id
),

图片

prep CTE 数据准备:

1.Board:当前的棋盘。

2.Masks:长度为27的位掩码数组,因为它一次性存储了 9****行 + 9****列 + 9****宫 的所有位掩码。

3.Emp:待填格子位置索引。

4.COALESCE 和 LEFT JOIN。为了代码的健壮性——即使输入的数独已经是满的(没有空位,empty_calc 为 NULL),代码也不会报错,而是生成一个空任务列表,直接流转到结束状态

第一阶段简单题快速求解

-- 5. 第一阶段:贪心极速流 (确定性求解)
solve_easy(id, board, ms, emp) AS (
    SELECT id, board, ms, emp FROM prep
    UNION ALL
    SELECT
        s.id,
        -- 1. 更新棋盘:把原来的 0 替换成新的数字
        s.board[:cell.p-1] || c.val::smallint || s.board[cell.p+1:],
        -- 2. 更新掩码:把新数字加入到对应的行、列、宫的黑名单里
        s.ms[:cell.r] || (s.ms[cell.r+1] | c.bit_m)::smallint || s.ms[cell.r+2:9+cell.c] || (s.ms[10+cell.c] | c.bit_m)::smallint || s.ms[11+cell.c:18+cell.bx] || (s.ms[19+cell.bx] | c.bit_m)::smallint || s.ms[19+cell.bx+1:27],
        ---- 3.移除已处理空格:从空位列表里移除当前位置
        array_remove(s.emp, cell.p)
    FROM solve_easy s
    INNER JOIN LATERAL (
        SELECT p, (p-1)/9 as r, (p-1)%9 as c, (p-1)/27*3+(p-1)%9/3 as bx, cand_m
        FROM unnest(s.emp) p   -- 展开所有空位(emp)
        CROSS JOIN LATERAL (   -- 计算候选掩码:全集(511) 剔除(AND NOT) 行列宫已有的数字
            SELECT 511 & ~(s.ms[(p-1)/9+1]::int | s.ms[(p-1)%9+10]::int | s.ms[(p-1)/27*3+(p-1)%9/3+19]::int) as cand_m
        ) t
        WHERE bit_count(cand_m::bit(9)) = 1     -- 【关键!】只要那些只有 1 个可能性的
        LIMIT 1                                 -- 每次只抓一个,保持稳健
    ) cell ON true
    CROSS JOIN LATERAL (
        -- 把二进制掩码转回 1-9 的十进制整数
        SELECT (bit_count(((cell.cand_m & -cell.cand_m) - 1)::bit(32)) + 1)::smallint as val,
               (cell.cand_m & -cell.cand_m)::int as bit_m
    ) c
),

A. 扫描与计算 (The Scanner)

•核心代码:511 & ~(Row | Col | Box)

•利用位运算瞬间计算补集。这相当于问数据库:在这个位置,除了行、列、宫里已有的数字,还剩下什么能填?

B. 贪心判定 (The Filter)

•代码:WHERE bit_count(...) = 1

•只有当某个格子在逻辑上只能填入一个数字时才行动。这保证了这一阶段的所有操作都是100% 正确的,不需要回溯。

C. 状态更新 (The Update)

•代码:s.ms[...] || ... (复杂的数组拼接)

•使用PostgreSQL 的数组切片操作,在填入数字时同时更新了行、列、宫的位掩码。这确保了下一次递归时,约束条件是最新的。

逻辑分流

-- 6. 逻辑分流
finished_easy AS (
    -- [快速通道] 只要任务清单空了,直接结算,释放内存
    SELECT id, board FROM solve_easy WHERE cardinality(emp) = 0
),
stuck_hard_input AS (
    -- [优胜劣汰] 筛选出卡住的难题
    SELECT DISTINCT ON (id) id, board, ms, emp
    FROM solve_easy
    WHERE id NOT IN (SELECT id FROM finished_easy)
    -- [关键策略] 如果有多个中间状态,取剩余空位最少的那一个继续算
    ORDER BY id, cardinality(emp) ASC
),

A. 内存保护 (Memory Safety)

•面对 1 万道题,如果让已完成的题目继续参与后续的递归连接,会造成巨大的内存浪费。通过 finished_easy 立刻卸载已完成的数据,相当于给系统做了‘内存减负’,防止大数据量下的 磁盘溢出

B. 优胜劣汰 (Best-First Selection)

•在第一阶段,可能会产生同一题目的不同中间状态。利用 ORDER BY cardinality ASC,强行只保留完成度最高的那一个状态进入下一轮。这确保了后续的回溯搜索是从最佳起点开始的。

• PostgreSQL 特有的 DISTINCT ON 语法(比排序后limit 1高)

第二阶段-剩余难题处理--回溯补刀 (MRV 策略)​​​​​​​

-- 7. 第二阶段:回溯补刀 (MRV 策略)
solve_hard(id, board, ms, emp) AS (
    SELECT id, board, ms, emp FROM stuck_hard_input  -- 接力棒:处理剩余的难题
    UNION ALL
    SELECT
        s.id,
        s.board[:cell.p-1] || v.val::smallint || s.board[cell.p+1:],
        s.ms[:cell.r] || (s.ms[cell.r+1] | v.bit_m)::smallint || s.ms[cell.r+2:9+cell.c] || (s.ms[10+cell.c] | v.bit_m)::smallint || s.ms[11+cell.c:18+cell.bx] || (s.ms[19+cell.bx] | v.bit_m)::smallint || s.ms[19+cell.bx+1:27],
        array_remove(s.emp, cell.p)
    FROM solve_hard s
    JOIN LATERAL (
        SELECT p, (p-1)/9 as r, (p-1)%9 as c, (p-1)/27*3+(p-1)%9/3 as bx, cand_m
        FROM unnest(s.emp) p
        CROSS JOIN LATERAL (
            SELECT 511 & ~(s.ms[(p-1)/9+1]::int | s.ms[(p-1)%9+10]::int | s.ms[(p-1)/27*3+(p-1)%9/3+19]::int) as cand_m
        ) t
        -- 【关键差异】不再寻找唯一解,而是寻找"可能性最少"的格子
        ORDER BY bit_count(cand_m::bit(9)) ASC
        LIMIT 1
    ) cell ON true
    JOIN LATERAL (
        -- 【关键差异】展开所有可能的候选数
        SELECT d+1 as val, 1<<d as bit_m
        FROM generate_series(0,8) d WHERE (cell.cand_m & (1<<d)) <> 0
    ) v ON true
)

图片

最终合并输出

-- 8. 最终合并输出
SELECT
    f.id,
    s.puzzle,   -- [延迟物化] 此时才去读取原始题面字符串,而非在复杂递归中带着消耗资源
    -- f.board,  --结果数组,方便验证数据,最终输出时注释掉
    -- 格式化输出: 数组转字符串,每9位换行
    trim(both E'\n' FROM regexp_replace(array_to_string(f.board, ''), '(.{9})', '\1' || E'\n', 'g')) as result
FROM (
    SELECT id, board FROM finished_easy
    UNION ALL
    -- [去重] 再次确保每题只留一个解
    SELECT DISTINCT ON (id) id, board FROM solve_hard WHERE cardinality(emp) = 0 ORDER BY id
) f
JOIN src s ON f.id = s.id;
 

A. 延迟物化 (Late Materialization)

在之前所有的递归计算中,没有携带 puzzle 这个原始字符串。因为字符串在内存中很占空间,且拷贝成本高。 只传递了 ID 和轻量级的 smallint 数组。直到最后一步,才通过 JOIN 把它拿回来。 这种延迟物化策略,可以节省一些内存提升性能。

B. 结果整形 (Result Formatting)

•利用了 PostgreSQL 强大的正则替换功能。 (.{9}) 匹配任意 9 个字符,\1\n 在其后插入换行符。这避免了写复杂的循环或应用层代码,直接在数据库层完成了数据的渲染


参赛完整SQL:

--用一条 SQL 解数独问题
--数据库:PostgreSQL
--提交人:周仟荣1
WITH RECURSIVE
-- 1. 数据源 & 强壮解析 (Robust Parsing)
src AS (
    SELECT
        id,
        puzzle,
        -- 1. regexp_replace: 剔除所有非数字、非点、非问号的字符 (包括空格、换行、制表符等)
        -- 2. translate: 再将 '.' 和 '?' 统一转为 '0'
        -- 3. 结果: 得到纯净的 81 位数字字符串
        string_to_array(
            translate(
                regexp_replace(puzzle, '[^0-9.?]', '', 'g'),
                '.?', '00'
            ),
            NULL
        )::smallint[] AS board
    FROM sudoku9_9 -- 题面
),
-- 2. 拆解单元格坐标
cells AS (
    SELECT
        id, val, i,   --id,原值,81位字符串序号
        (i-1)/9 as r,   --行
        (i-1)%9 as c,   --列
        ((i-1)/27)*3 + ((i-1)%9)/3 as bx  --宫
    FROM src, unnest(board) WITH ORDINALITY as t(val, i)   --数组展开为行
),
-- 3. 批量初始化掩码 (Filter 聚合)
masks_calc AS (----使用“位掩码”技术。通过位运算(Bitwise OR)将每一行、列、宫中已有的数字记录在一个二进制数中
    SELECT id, array_agg(COALESCE(m, 0)::smallint ORDER BY idx) as ms
    FROM (
        SELECT id, r+1 as idx, bit_or(1 << (val-1)) FILTER (WHERE val>0) as m FROM cells GROUP BY id, r
        UNION ALL
        SELECT id, c+10 as idx, bit_or(1 << (val-1)) FILTER (WHERE val>0) as m FROM cells GROUP BY id, c
        UNION ALL
        SELECT id, bx+19 as idx, bit_or(1 << (val-1)) FILTER (WHERE val>0) as m FROM cells GROUP BY id, bx
    ) t
    GROUP BY id
),
    ---待填格子位置索引:筛选出所有值为 0 的格子索引,打包为一个数组
empty_calc AS (
    SELECT id, array_agg(i::smallint ORDER BY i) as emp
    FROM cells WHERE val = 0 GROUP BY id
),
-- 4. 组装初始状态--递归前数据准备
prep AS (
    SELECT
        s.id, s.board,  --[零件1] 原始棋盘
        -- [零件2] 约束掩码 (27个整数)
        -- 这里包含了 9行+9列+9宫 的初始状态
        COALESCE(m.ms, '{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}'::smallint[]) as ms,
        -- [零件3] 待办空位数组 (递归的任务列表)
        COALESCE(e.emp, '{}'::smallint[]) as emp
    FROM src s
     -- 将三个独立的数据流合并为一行
    LEFT JOIN masks_calc m ON s.id = m.id
    LEFT JOIN empty_calc e ON s.id = e.id
),
-- 5. 第一阶段:贪心极速流 (确定性求解)
solve_easy(id, board, ms, emp) AS (
    SELECT id, board, ms, emp FROM prep
    UNION ALL
    SELECT
        s.id,
        -- 1. 更新棋盘:把原来的 0 替换成新的数字
        s.board[:cell.p-1] || c.val::smallint || s.board[cell.p+1:],
        -- 2. 更新掩码:把新数字加入到对应的行、列、宫的黑名单里
        s.ms[:cell.r] || (s.ms[cell.r+1] | c.bit_m)::smallint || s.ms[cell.r+2:9+cell.c] || (s.ms[10+cell.c] | c.bit_m)::smallint || s.ms[11+cell.c:18+cell.bx] || (s.ms[19+cell.bx] | c.bit_m)::smallint || s.ms[19+cell.bx+1:27],
        ---- 3.移除已处理空格:从空位列表里移除当前位置
        array_remove(s.emp, cell.p)
    FROM solve_easy s
    INNER JOIN LATERAL (
        SELECT p, (p-1)/9 as r, (p-1)%9 as c, (p-1)/27*3+(p-1)%9/3 as bx, cand_m
        FROM unnest(s.emp) p   -- 展开所有空位(emp)
        CROSS JOIN LATERAL (   -- 计算候选掩码:全集(511) 剔除(AND NOT) 行列宫已有的数字
            SELECT 511 & ~(s.ms[(p-1)/9+1]::int | s.ms[(p-1)%9+10]::int | s.ms[(p-1)/27*3+(p-1)%9/3+19]::int) as cand_m
        ) t
        WHERE bit_count(cand_m::bit(9)) = 1     -- 【关键!】只要那些只有 1 个可能性的
        LIMIT 1                                 -- 每次只抓一个,保持稳健
    ) cell ON true
    CROSS JOIN LATERAL (
        -- 把二进制掩码转回 1-9 的十进制整数
        SELECT (bit_count(((cell.cand_m & -cell.cand_m) - 1)::bit(32)) + 1)::smallint as val,
               (cell.cand_m & -cell.cand_m)::int as bit_m
    ) c
),
-- 6. 逻辑分流
finished_easy AS (
    -- [快速通道] 只要任务清单空了,直接结算,释放内存
    SELECT id, board FROM solve_easy WHERE cardinality(emp) = 0
),
stuck_hard_input AS (
    -- [优胜劣汰] 筛选出卡住的难题
    SELECT DISTINCT ON (id) id, board, ms, emp
    FROM solve_easy
    WHERE id NOT IN (SELECT id FROM finished_easy)
    -- [关键策略] 如果有多个中间状态,取剩余空位最少的那一个继续算
    ORDER BY id, cardinality(emp) ASC
),
-- 7. 第二阶段:回溯补刀 (MRV 策略)
solve_hard(id, board, ms, emp) AS (
    SELECT id, board, ms, emp FROM stuck_hard_input  -- 接力棒:处理剩余的难题
    UNION ALL
    SELECT
        s.id,
        s.board[:cell.p-1] || v.val::smallint || s.board[cell.p+1:],
        s.ms[:cell.r] || (s.ms[cell.r+1] | v.bit_m)::smallint || s.ms[cell.r+2:9+cell.c] || (s.ms[10+cell.c] | v.bit_m)::smallint || s.ms[11+cell.c:18+cell.bx] || (s.ms[19+cell.bx] | v.bit_m)::smallint || s.ms[19+cell.bx+1:27],
        array_remove(s.emp, cell.p)
    FROM solve_hard s
    JOIN LATERAL (
        SELECT p, (p-1)/9 as r, (p-1)%9 as c, (p-1)/27*3+(p-1)%9/3 as bx, cand_m
        FROM unnest(s.emp) p
        CROSS JOIN LATERAL (
            SELECT 511 & ~(s.ms[(p-1)/9+1]::int | s.ms[(p-1)%9+10]::int | s.ms[(p-1)/27*3+(p-1)%9/3+19]::int) as cand_m
        ) t
        -- 【关键差异】不再寻找唯一解,而是寻找"可能性最少"的格子
        ORDER BY bit_count(cand_m::bit(9)) ASC
        LIMIT 1
    ) cell ON true
    JOIN LATERAL (
        -- 【关键差异】展开所有可能的候选数
        SELECT d+1 as val, 1<<d as bit_m
        FROM generate_series(0,8) d WHERE (cell.cand_m & (1<<d)) <> 0
    ) v ON true
)
-- 8. 最终合并输出
SELECT
    f.id,
    s.puzzle,   -- [延迟物化] 此时才去读取原始题面字符串,而非在复杂递归中带着消耗资源
    -- f.board,  --结果数组,方便验证数据,最终输出时注释掉
    -- 格式化输出: 数组转字符串,每9位换行
    trim(both E'\n' FROM regexp_replace(array_to_string(f.board, ''), '(.{9})', '\1' || E'\n', 'g')) as result
FROM (
    SELECT id, board FROM finished_easy
    UNION ALL
    -- [去重] 再次确保每题只留一个解
    SELECT DISTINCT ON (id) id, board FROM solve_hard WHERE cardinality(emp) = 0 ORDER BY id
) f
JOIN src s ON f.id = s.id;
 

《数据库编程大赛》

下一次再聚!

感谢大家对本次《数据库编程大赛》的关注和支持,欢迎加入技术交流群,更多精彩活动不断,欢迎各路数据库爱好者来挑战!

相关推荐

2026最新PostgreSQL到Doris数据同步完整教程:全流程图形化配置无需写ETL

本文演示通过NineData控制台将PostgreSQL数据实时同步至Apache Doris的完整流程,涵盖结构、全量、增量三阶段复制。全程图形化操作,无需编写脚本,支持键列与分布键可视化映射、复制规格预检查、数据对比校验及延迟告警。适用于构建稳定高效的实时分析数仓,助力报表与大屏查询基于最新数据。

云原生智能数据管理平台 154

10 月更新|NineData 新增 Vastbase、SelectDB、DB2 等异构链路,优化 Oracle 增量解析性能

10月,NineData持续强化数据库DevOps、AIAgent与数据复制能力。新增18条数据复制与对比链路,支持多引擎异构迁移,如TiDB→DB2、OpenGauss→PolarDB等。DevOps扩展Databricks支持,优化SQL规范校验、WebSocket缓冲与OnlineDML功能。权限流程升级,支持API获取数据源,修复工作流状态一致性。AIAgent优化模型配置与ChatDBA交互体验。数据复制增强增量写入、容灾与Oracle性能,支持TiCDC集群源与字段长度超限处理。SQL窗口修复多

云原生智能数据管理平台 358

NineData 五周年团建 | 让数据奔涌,热爱无界!

2026年11月1日,NineData五周年团建在千岛湖举行。团队乘船启航,登梅峰揽胜,俯瞰群岛如珠,共庆成长;随后开展无人机拍摄、趣味游戏等精彩活动,展现程序员的多元风采。从数据智能平台到“数据库+AI”融合创新,五年来,NineData以“代码有解,热爱无界”为信念,持续赋能金融、制造、AI等行业客户。感恩同行,奔赴新程,未来将继续让数据自由奔涌,向更辽阔山海进发。

云原生智能数据管理平台 168

NineData 完成飞腾处理器兼容性认证,夯实信创数据管理底座

玖章算术旗下NineData云原生智能数据管理平台完成与飞腾处理器的兼容性认证,实现国产算力底座上的高效稳定运行。双方在数据开发、安全管控等全流程中表现优异,标志着“国产芯片+国产软件”全栈信创解决方案再添关键拼图。NineData已全面适配鲲鹏、麒麟、达梦等主流国产软硬件,助力政企低成本、高效率推进数字化转型。

云原生智能数据管理平台 285

NineData 社区版 V5.4.0 上线:715 条复制对比链路,AI Agent 新增表结构设计发布能力

NineData社区版V5.4.0升级聚焦数据库DevOps与数据复制能力:新增GBase8A、Snowflake数据源支持,扩展敏感数据保护与SQL任务覆盖范围,优化权限审批与治理体验;数据复制链路新增16条,累计达715条,支持全品类数据源,Kafka目标端兼容Debezium格式,增量复制性能提升超400%;AIAgent增强,新增结构设计与发布能力,ChatDBA支持消息排队与后台任务执行;基础服务新增脚本自定义通知,部署便捷,免费开源,助力企业高效安全的数据管理。

云原生智能数据管理平台 226

告别 SQL 幻觉、越权风险:NineData 打通 AI Agent 访问 Apache Doris 最后一公里

在DorisSummit2026上,NineData合伙人蔡冬者分享了Apache Doris实时数据同步的演进路径。面对Agent时代对数据秒级入仓、语义理解与安全访问的新挑战,NineData提出“一站式数据流动与治理”方案,涵盖海量异构数据实时同步、智能表结构迁移、内置ETL与数据对比,并通过“业务语义层+安全访问控制”双轮驱动,实现Agent用数的精准、高效与可控。案例显示,该方案已在金融、政务、物流及大模型企业中成功落地,助力企业安全迈入AI驱动的数据智能新阶段。

云原生智能数据管理平台 248

玖章算术(NineData) 成为中国通信学会会员,共建信息通信产业创新生态

玖章算术(NineData)正式成为中国通信学会单位会员,彰显其在数据管理与信息通信领域的技术实力。公司聚焦云原生智能数据管理,推出NineData平台,助力通信、金融、制造等行业实现高效、安全的数据协同。依托与中国移动、电信、联通等深度合作,持续推动AI驱动的数据管理创新。加入学会后,将积极参与标准制定与产教融合,共促数字中国建设。

云原生智能数据管理平台 183

MySQL 历史数据如何归档和定期清理?NineData 配置与实施指南

使用 NineData 配置 MySQL 历史数据归档与定期清理的方法,覆盖归档策略、时间字段、保留天数、业务过滤、周期调度、SQL 预览、冲突处理、审批及 Optimize 空间回收注意事项。

云原生智能数据管理平台 288

用 NineData 完成 SQL Server 到 ClickHouse 历史数据初始化

本文介绍从SQLServer向ClickHouse迁移历史数据的初始化流程,涵盖对象选择、结构映射核对及目标数据检查。通过明确分析范围、确认表结构与权限,分阶段完成结构准备与数据初始化,确保报表、趋势分析等场景的数据可用性。强调需规避全库迁移,优先筛选关键业务数据,任务前评估资源与已有数据处理策略,保障数据准备清晰可控。

云原生智能数据管理平台 234

MySQL 迁移到 OceanBase 怎么做?NineData 全量迁移、CDC 实时同步与数据校验指南

本文介绍使用 NineData 将 MySQL 低停机迁移到 OceanBase MySQL 的实施流程,覆盖社区版版本要求、Binlog 准备、结构与全量复制、CDC 实时同步、模板导入、数据一致性校验及生产割接回退建议。

云原生智能数据管理平台 376

Oracle 数据实时同步到 StarRocks:全量迁移、CDC 增量入仓与数据校验实践

Oracle 实时同步到 StarRocks,需要把结构、存量、日志 CDC、目标表模型和数据校验作为一个整体设计。NineData 提供 Oracle→StarRocks 专属复制链路。

云原生智能数据管理平台 465

倒计时 5 天|Doris Summit 2026 预告:NineData 蔡冬者分享海量异构数据实时同步至 Doris 实践

2026年10月22日,Doris Summit 2026全球直播开启,NineData合伙人蔡冬者将分享《Apache Doris 实时数据库同步方案的挑战与实践》。聚焦AI时代下海量异构数据高效同步,详解NineData在CDC增量复制、轻量ETL及安全访问控制方面的创新,助力企业实现高吞吐、低延迟的数据集成与安全用数。峰会汇聚全球专家,共探实时智能与湖仓架构演进。

云原生智能数据管理平台 171

NineData 云原生智能数据管理平台适配 Vastbase G100,支撑国产数据库研发与迁移落地

玖章算术NineData V5.0与海量数据库VastbaseG100 V3.0完成兼容认证,实现数据源接入、迁移、SQL执行等全链路无缝适配。此次合作标志着国产数据库从“内核可用”迈向“场景落地、生态协同”的关键一步,彰显“内核+工具链+生态”整体成熟。NineData持续深耕信创生态,已适配鲲鹏、海光、麒麟、达梦等主流国产软硬件,助力企业构建安全可控的数据底座,推动国产化数据库在政企领域的规模化应用。

云原生智能数据管理平台 396

NineData智能数据管理平台新功能发布|2026年9月

NineData本月重点扩展数据库DevOps的数据源与SQL开发能力,新增OpenSearch、Snowflake及GBase8A支持,完善SQL任务、敏感数据与权限管理。ChatDBA新增Excel知识库与结构设计Skill,支持消息排队与后台对话。数据复制新增Debezium格式与双向防环机制,优化稳定性与重试策略。修复多项数据对比、导出及兼容性问题,提升TiDB、OceanBase等多引擎性能诊断能力。功能覆盖全面,持续强化数据资产与审批流程管理。

云原生智能数据管理平台 217

Oracle 实时同步到 Doris 怎么做?全量迁移、CDC 增量复制与数据校验指南

本文介绍如何使用 NineData 将 Oracle 实时同步到 Apache Doris,覆盖 Oracle 归档日志与附加日志配置、全量迁移、CDC 增量复制、Oracle 到 Doris 类型映射、Key 与 Distribute Key、同步延迟监控,以及全量和增量数据一致性校验。

云原生智能数据管理平台 447

MySQL 到 Doris 实时数据同步:订单、用户和日志数据入仓方案

订单、用户、日志数据怎么从 MySQL 实时进 Doris?本文讲清一条完整链路:表模型选型、目标表创建、全量迁移、Binlog 增量同步、数据校验与上线验证,并用 NineData 的可视化复制任务替代导出脚本 + 消费程序 + 写入任务的多套维护方式。

云原生智能数据管理平台 335

NineData亮相2026新加坡Tech Week,以数据管理AI Agent加速布局全球市场

9月29日至30日,新加坡科技周2026在滨海湾金沙会展中心举行。浙江企业NineData亮相展会,展示云原生智能数据管理平台三大核心能力:企业级数据库DevOps、多云数据复制与对比、AI数据管理AIAgent。依托浙江省商务厅组织出海,NineData借力新加坡区位优势,面向亚太及全球市场,助力跨国企业实现高效、安全的数据协同与全球化布局。现场获金融、制造、互联网等领域客户高度关注,多家海外伙伴达成合作意向,持续推动智能数据管理技术在全球落地。

云原生智能数据管理平台 223

NineData 与统信服务器操作系统完成兼容认证

玖章算术NineData V5.0与统信服务器操作系统V25完成兼容性认证,实现核心功能稳定运行,助力信创生态升级。作为国产数据管理标杆,NineData全面适配国产软硬件,支持百种数据源与云环境,融合AI能力提升数据治理效率。携手统信软件等伙伴,深化全栈协同,推动政企数字化转型与自主可控发展。

云原生智能数据管理平台 314

Oracle到OceanBase MySQL:NineData一键搞定结构复制+全量复制+增量复制+数据对比教程

NineData支持Oracle到OceanBase MySQL数据复制,可将结构复制、全量复制、增量复制和数据对比纳入同一条迁移链路。企业可以先准备目标端结构,再完成历史数据初始化,随后持续同步业务变化,让异构数据库迁移按阶段推进。

云原生智能数据管理平台 245

玖章算术NineData完成海光CPU生态兼容性认证,深化国产算力生态适配

玖章算术旗下 NineData 云原生智能数据管理平台与海光 CPU 完成生态兼容性认证。经双方联合测评,NineData 在海光 CPU 处理器环境下各项核心功能运行稳定,产品适配完善,可实现专属集群完整部署落地,具备优秀的软硬件兼容适配能力,共同为企业打造全栈自主可控的数据基础设施解决方案。

云原生智能数据管理平台 236

NineData智能数据管理平台新功能发布|2026年8月

8月,NineData持续优化数据库DevOps平台,重点实现AI辅助SQL开发与ChatDBA深度联动,支持多数据源接入(如ShardingProxy、AWS Redshift、HaishanDB等),扩展数据导入导出能力,并强化审批治理与复制稳定性。AI可智能生成、回填SQL,提升开发效率;复制任务支持自动化对比校验,按表攒批与并发写入优化显著提升性能;同时增强权限管理、DDL转换及连接状态监控,全面保障数据流转安全高效。

云原生智能数据管理平台 264

NineData 社区版 V5.3.0 正式发布!131 条新增数据链路 + DevOps 全能力升级(附 Docker 一键部署命令)

NineData社区版V5.3.0发布,核心升级包括:AIAgent新增数据导入导出能力,ChatDBA扩展多库性能诊断与慢SQL优化;数据复制新增131条链路,支持OLTP→Kafka、KingbaseOracle大事务性能提升一倍;数据库DevOps全面扩容,新增多款国产及海外数据源,强化CI/CD、权限管理、敏感数据保护与审计能力。全功能免费,Docker一键部署,助力企业高效安全地实现数据管理与迁移。

云原生智能数据管理平台 118

NineData 实现 Db2 到 ClickHouse 全量增量持续数据复制

NineData 的 Db2 到 ClickHouse 复制链路是一条由结构复制、全量复制、增量复制和数据对比组成的持续数据链路。它可以帮助团队先完成历史数据准备,再把后续业务变化持续传递到分析端。如果您正在建设报表或 BI 平台,或者需要将 Db2 数据迁移到更适合分析查询的目标端,可以先从一个明确的业务主题或一组核心表开始创建复制任务,确认对象映射、数据结果和增量延迟后,再逐步扩展复制范围。

云原生智能数据管理平台 235

NineData 与平凯数据库(TiDB 企业版)完成产品兼容性互认证

玖章算术旗下 NineData 云原生智能数据管理平台与平凯数据库(TiDB企业版) 完成兼容性认证。经双方联合测评,双方产品完全相互兼容、功能完善、整体运行稳定,可无缝集成,具备良好的兼容适配能力,共同为企业提供一站式的数据管理解决方案。

云原生智能数据管理平台 325

ACDU·中国行北京站倒计时 3 天,NineData 分享 AI 赋能海山数据库迁移实践与全链路管控

面对异构数据库迁移,团队往往同时承受评估复杂、SQL 改写耗时、切换风险高等压力。8 月 29 日北京站现场,NineData 合伙人 & 首席产品经理蔡冬者将带来《Agent 时代的数据架构演进:AI 赋能海山数据库迁移实践与全链路管控》主题分享。

云原生智能数据管理平台 225

DTCC 2026 NineData 叶正盛:如何统一管理人与 AI Agent 的数据访问行为

NineData亮相2026中国数据库技术大会,CEO叶正盛发表主题演讲,探讨AIAgent时代下的数据库管理新挑战与解决方案。随着企业数据环境日益复杂(多数据库、多云架构、AIAgent驱动),NineData提出通过云原生智能数据管理平台整合数据库DevOps、数据复制和AI能力,构建统一治理体系。平台具备细粒度权限管控、自动化规范检查、全链路审计等核心功能,支持100+数据源和秒级跨地域同步,已应用于跨境支付、金融科技等多个行业场景,实现效率提升与安全管控的平衡。演讲强调,在AIAgent成为新数据消

云原生智能数据管理平台 457

NineData Oracle 到 ClickHouse 数据迁移完整流程:结构复制、全量装载、增量同步

本文介绍了将Oracle核心业务数据迁移至ClickHouse的解决方案。通过NineData工具可实现Oracle到ClickHouse的数据结构、全量和增量同步,将分析查询与业务处理分离。该方案适用于订单、客户等历史数据分析、报表查询、测试开发等场景,能有效减轻Oracle负载。实施前需确保数据库版本、权限、日志模式等条件满足,并合理规划表结构和数据处理策略。这种数据通道让Oracle专注事务处理,ClickHouse承担分析查询,降低人工操作成本,提高数据分析效率。

云原生智能数据管理平台 321

DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践

NineData将亮相2026中国数据库技术大会(DTCC2026),创始人兼CEO叶正盛将于8月21日上午在主会场发表《面向AIAgent的数据库DevOps与数据复制思考实践》主题演讲。作为阿里云前数据库产品负责人,叶正盛将分享AIAgent在数据库运维中的安全防护与智能应用实践,探讨如何构建高效可控的数据库访问体系,并展示从SQL审核到实时数据协同的全生命周期解决方案。NineData作为新一代智能数据管理平台,已服务1000+企业,管理超10万数据库实例。本届大会以"融数聚智创领未来&qu

云原生智能数据管理平台 322

NineData 7 月更新把 SQL 开发和复制稳定性继续往前推

NineData 7月更新聚焦多源数据库管理痛点,通过三大核心优化提升全链路效率:1. 数据源扩展:新增MaxCompute、AWS Redshift等6种数据库支持,完善PG系功能,实现跨库统一开发体验;2. 治理体系强化:慢查询分析覆盖OceanBase等8类数据库,敏感数据扫描新增GaussDB,审批流程支持节点自定义;3. 复制能力升级:支持YashanDB与Kafka异构同步,优化Oracle/PolarDB等高并发场景的依赖处理,任务稳定性提升40%。同步改进AI交互与模型配置,形成覆盖连接、开

云原生智能数据管理平台 314

Oracle 长事务正在消耗 undo 空间,ChatDBA 能提前发现

文章摘要:Oracle数据库长事务是常见隐患,表现为undo表空间持续增长、业务变慢却无报错。ChatDBA工具通过五维度分析(事务时长、锁阻塞、undo压力等)智能诊断风险,提供可对话的处置建议(观察/提交/回滚/Kill),并评估操作代价。它将专家经验转化为标准流程,支持历史案例参考,帮助团队在事务早期发现风险,避免后期回滚时间非线性增长造成的生产事故。使用时可登录NineData平台,勾选深度分析选项获取完整事务链信息。

云原生智能数据管理平台 322
上一篇: SQL编程大赛第6名张泽鹏:数独还能这么解?PostgreSQL 玩转约束传播
下一篇: SQL编程大赛第4名马尾蛟:一条 SQL 解数独?PostgreSQL 递归 CTE 封神思路全拆解
NineData
NineData NineData官方产品账号 NineData官方产品账号
博客等级 码龄4年 3806粉丝 425原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值