Sudoku solver in pure SQL
WITH RECURSIVE
input(sud) AS (
VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79')
),
digits(z, lp) AS (
VALUES('1', 1)
UNION ALL SELECT
CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
),
x(s, ind) AS (
SELECT sud, instr(sud, '.') FROM input
UNION ALL
SELECT
substr(s, 1, ind-1) || z || substr(s, ind+1),
instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )
FROM x, digits AS z
WHERE ind>0
AND NOT EXISTS (
SELECT 1
FROM digits AS lp
WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)
OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1)
OR z.z = substr(s, (((ind-1)/3) % 3) * 3
+ ((ind-1)/27) * 27 + lp
+ ((lp-1) / 3) * 6, 1)
)
)
SELECT s FROM x WHERE ind=0;
It is one of the examples of Recursive CTE from SQLite docs
Let's step in step by step and see how it is working
WITH RECURSIVE Clause
The query begins with the `WITH RECURSIVE` clause, which allows us to define common table expressions (CTEs) that can refer to themselves. This is crucial for handling recursive operations, like the backtracking needed to solve a Sudoku puzzle.
Input CTE
sql
input(sud) AS (
VALUES('53..7….6..195….98….6.8…6…34..8.3..17…2…6.6….28….419..5….8..79')
)
The `input` CTE defines the starting Sudoku grid. The grid is represented as a string of 81 characters (9x9 grid), where each digit represents a filled cell, and dots (`.`) represent empty cells that need to be filled.
- **Structure**: In this example, the string `’53..7….6..195….98….6.8…6…34..8.3..17…2…6.6….28….419..5….8..79'` is the puzzle input.
Digits CTE
digits(z, lp) AS (
VALUES('1', 1)
UNION ALL SELECT
CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
)
The `digits` CTE generates a list of digits (`1` to `9`) along with their positions.
— `VALUES(‘1’, 1)` initializes the sequence with the first digit `1`.
— `UNION ALL` recursively adds the next digit by incrementing the position until it reaches `9`.
X CTE
x (s, ind) AS (
SELECT sud, instr(sud, '.') FROM input
UNION ALL
SELECT
substr(s, 1, ind-1) || z || substr(s, ind+1),
instr(substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )
FROM x, digits AS z
WHERE ind>0
AND NOT EXISTS (
SELECT 1
FROM digits AS lp
WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)
OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1)
OR z.z = substr(s, (((ind-1)/3) % 3) * 3
+ ((ind-1)/27) * 27 + lp
+ ((lp-1) / 3) * 6, 1)
)
)
The `x` CTE is the core of the recursive logic, responsible for filling the Sudoku grid.
Initial Step
— `SELECT sud, instr(sud, ‘.’) FROM input` finds the position of the first empty cell (`.`) in the input string.
Recursive Step
— `substr(s, 1, ind-1) || z || substr(s, ind+1)` creates a new string by replacing the `.` at position `ind` with a digit `z`.
— `instr(substr(s, 1, ind-1) || z || substr(s, ind+1), ‘.’)` finds the position of the next empty cell after filling the current one.
— The `WHERE` clause checks two conditions:
1. `ind > 0` ensures that there are still empty cells to fill.
2. `NOT EXISTS` ensures that the digit `z` being placed does not violate the Sudoku rules (i.e., it does not appear in the same row, column, or 3x3 subgrid).
Final SELECT Statement
SELECT s FROM x WHERE ind=0;
The final `SELECT` statement retrieves the solved Sudoku grid.
The query selects the string `s` from the `x` CTE where `ind=0`, meaning all cells have been filled (no more `.` in the string), indicating that the puzzle is solved.
How the Query Works as a Whole
1. The puzzle is input as a string with digits and dots.
2. Digit Generation: A list of digits from `1` to `9` is generated for substitution.
3. Recursive Filling: The grid is filled recursively:
— The query attempts to place each digit in the first empty position.
— If the digit placement is valid (i.e., it doesn’t violate Sudoku rules), it proceeds to the next empty position.
— This process continues until the entire grid is filled.
4. Result: The solved puzzle is returned once the grid is filled without any violations.
Conclusion
This SQL query showcases a creative use of recursive common table expressions to solve a classic problem. While SQL is typically used for data retrieval and manipulation, this query demonstrates its power and flexibility, even in solving puzzles like Sudoku. The recursive approach allows the query to try different possibilities, backtrack when necessary, and ultimately find the solution.