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 ?

Capture d’écran 2010-12-15 à 18.17.01
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 Documentation
Lucas Jellema 's Blog
Fibonaci Numbers

© Oracle Blogs or respective owner

Related posts about database

Related posts about 11gR2