Any better Fibonacci series generator using pure Oracle SQL?

前端 未结 3 2015
清酒与你
清酒与你 2021-01-19 23:08

I wonder if there is any way to generate Fibonacci numbers that beat in simplicity and efficiency this one I wrote:

WITH d (seq) AS
       (SELECT     LEVEL
         


        
相关标签:
3条回答
  • 2021-01-19 23:27

    You can use a recursive sub-query factoring clause:

    WITH fib ( lvl, value, next ) AS (
      SELECT 1, 0, 1
      FROM DUAL
    UNION ALL
      SELECT lvl + 1, next, value + next
      FROM fib
      WHERE lvl < 195
    )
    SELECT lvl, value FROM fib
    
    0 讨论(0)
  • 2021-01-19 23:32

    on the simplicity side of things, the query can rely on the built in features (ITERATE () and ITERATION_NUMBER) of MODEL:

    select * from dual
    model
      dimension by (0 seq)
      measures (0 val)
      rules iterate (195) 
      (
         val[iteration_number] = val[iteration_number-1] + val[iteration_number-2],
         val[2] = 1, 
         val[1] = 0, 
         val[0] = 0
      )
    ;
    
    0 讨论(0)
  • 2021-01-19 23:45

    Something like this should be (much?) faster:

    with
         constants ( x, y, z ) as (
           select 0.5 * ( 1 + sqrt(5) ),
                  0.5 * ( 1 - sqrt(5) ),
                  sqrt(5)
           from   dual
         )
    select level as seq, round( ( power(x, level - 1) - power(y, level - 1) ) / z ) as fib
    from   constants
    connect by level < 195
    ;
    

    The point being, you don't need to use the recursive formula; the terms can be written in closed form. Since computers can't do arithmetic with real numbers, only with rational number approximations, I needed to add a ROUND(...) but even so this should be faster than recursive approaches.

    EDIT: At the OP's request I traced the execution of this code. I don't see the recursive calls the OP is referring to in the Comment below.

    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 1236776825
    
    -----------------------------------------------------------------------------
    | Id  | Operation                    | Name | Rows  | Cost (%CPU)| Time     |
    -----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT             |      |     1 |     2   (0)| 00:00:01 |
    |*  1 |  CONNECT BY WITHOUT FILTERING|      |       |            |          |
    |   2 |   FAST DUAL                  |      |     1 |     2   (0)| 00:00:01 |
    -----------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(LEVEL<195)
    
    
    Statistics
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
              0  consistent gets
              0  physical reads
              0  redo size
           6306  bytes sent via SQL*Net to client
            684  bytes received via SQL*Net from client
             14  SQL*Net roundtrips to/from client
              1  sorts (memory)
              0  sorts (disk)
            194  rows processed
    

    EDIT #2

    I suspect the simple generation of levels in a recursive query may be expensive. It's possible that a cross-join of similarly generated, but smaller sequences of integers may work a bit faster. The code looks more complicated (of course); the only change, though, is the way I generate the powers.

    with
         constants ( x, y, z ) as (
           select 0.5 * ( 1 + sqrt(5) ),
                  0.5 * ( 1 - sqrt(5) ),
                  sqrt(5)
           from   dual
         ),
         powers ( n ) as (
           select 14 * a.p + b.q
           from   (select level - 1 p from dual connect by level <= 14) a
                  cross join
                  (select level - 1 q from dual connect by level <= 14) b
         )
    select n + 1 as seq, round( ( power(x, n) - power(y, n) ) / z ) as fib
    from   constants cross join powers
    where  n < 195
    ;
    
    0 讨论(0)
提交回复
热议问题