Any better Fibonacci series generator using pure Oracle SQL?

前端 未结 3 2040
清酒与你
清酒与你 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: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
    ;
    

提交回复
热议问题