Do you play Sudoku ?
Posted
by Gilles Haro
on Oracle Blogs
See other posts from Oracle Blogs
or by Gilles Haro
Published on Thu, 16 Dec 2010 19:00:00 +0000
Indexed on
2010/12/16
21:11 UTC
Read the original article
Hit count: 456
Did you know that 11gR2 database could solve a Sudoku puzzle with a single query and, most of the time, and this in less than a second ?
The following query shows you how !
Simply pass a flattened Sudoku grid to it a get the result instantaneously !
col "Solution" format a9
col "Problem" format a9
with Iteration( initialSudoku, Step, EmptyPosition ) as
( select initialSudoku, InitialSudoku, instr( InitialSudoku, '-' )
from ( select '--64----2--7-35--1--58-----27---3--4---------4--2---96-----27--7--58-6--3----18--' InitialSudoku from dual )
union all
select initialSudoku
, substr( Step, 1, EmptyPosition - 1 ) || OneDigit || substr( Step, EmptyPosition + 1 )
, instr( Step, '-', EmptyPosition + 1 )
from Iteration
, ( select to_char( rownum ) OneDigit from dual connect by rownum <= 9 ) OneDigit
where EmptyPosition > 0
and not exists
( select null
from ( select rownum IsPossible from dual connect by rownum <= 9 )
where OneDigit = substr( Step, trunc( ( EmptyPosition - 1 ) / 9 ) * 9 + IsPossible, 1 ) -- One line must contain the 1-9 digits
or OneDigit = substr( Step, mod( EmptyPosition - 1, 9 ) - 8 + IsPossible * 9, 1 ) -- One row must contain the 1-9 digits
or OneDigit = substr( Step, mod( trunc( ( EmptyPosition - 1 ) / 3 ), 3 ) * 3 -- One square must contain the 1-9 digits
+ trunc( ( EmptyPosition - 1 ) / 27 ) * 27 + IsPossible
+ trunc( ( IsPossible - 1 ) / 3 ) * 6 , 1 )
)
)
select initialSudoku "Problem", Step "Solution"
from Iteration
where EmptyPosition = 0 ;
The Magic thing behind this is called Recursive Subquery Factoring.
The Oracle documentation gives the following definition:
If a subquery_factoring_clause refers to its own query_name in the subquery that defines it, then the subquery_factoring_clause is said to be recursive.
A recursive subquery_factoring_clause must contain two query blocks:
the first is the anchor member and the second is the recursive member.
The anchor member must appear before the recursive member, and it cannot reference query_name.
The anchor member can be composed of one or more query blocks combined by the set operators: UNION ALL, UNION, INTERSECT or MINUS.
The recursive member must follow the anchor member and must reference query_name exactly once.
You must combine the recursive member with the anchor member using the UNION ALL set operator.
This new feature is a replacement of this old Hierarchical Query feature that exists in Oracle since the days of Aladdin (well, at least, release 2 of the database in 1977).
Everyone remembers the old syntax :
select empno, ename, job, mgr, level
from emp
start with mgr is null
connect by prior empno = mgr;
that could/should be rewritten (but not as often as it should) as
withT_Emp (empno, name, level) as
( select empno, ename, job, mgr, level
from emp
start with mgr is null
connect by prior empno = mgr
)
select * from T_Emp;
which uses the "with" syntax, whose main advantage is to clarify the readability of the query.
Although very efficient, this syntax had the disadvantage of being a Non-Ansi Sql Syntax.
Ansi-Sql version of Hierarchical Query is called Recursive Subquery Factoring.
As of 11gR2, Oracle got compliant with Ansi Sql and introduced Recursive Subquery Factoring. It is basically an extension of the "With" clause that enables recursion.
Now, the new syntax for the query would be
with T_Emp (empno, name, job, mgr, hierlevel) as
( select E.empno, E.ename, E.job, E.mgr, 1 from emp E where E.mgr is null
union all
select E.empno, E.ename, E.job, E.mgr, T.hierlevel + 1from emp E
join T_Emp T on ( E.mgr = T.empno )
)
select * from T_Emp;
The anchor member is a replacement for the "start with"
The recursive member is processed through iterations.
It joins the Source table (EMP) with the result from the Recursive Query itself (T_Emp)
Each iteration works with the results of all its preceding iterations.
Iteration 1 works on the results of the first query
Iteration 2 works on the results of Iteration 1 and first query
Iteration 3 works on the results of Iteration 1, Iteration 2 and first query.
So, knowing that, the Sudoku query it self-explaining;
The anchor member contains the "Problem" : The Initial Sudoku and the Position of the first "hole" in the grid.
The recursive member tries to replace the considered hole with any of the 9 digit that would satisfy the 3 rules of sudoku
Recursion progress through the grid until it is complete.
Another example : Fibonaccy Numbers : un = (un-1) + (un-2)
with Fib (u1, u2, depth) as
(select 1, 1, 1 from dual
union all
select u1+u2, u1, depth+1 from Fib where depth<10)
select u1 from Fib;
Conclusion
Oracle brings here a new feature (which, to be honest, already existed on other concurrent systems) and extends the power of the database to new boundaries. It’s now up to developers to try and test it and find more useful application than solving puzzles…
But still, solving a Sudoku in less time it takes to say it remains impressive…
Interesting links:
You might be interested by the following links which cover different aspects of this feature
© Oracle Blogs or respective owner