Monday, November 22, 2021

Inline Functions (Built into a 'SELECT').

Since Oracle 12, it's possible to include a function declaration right in your query. You define it much like a normal function, but it is included right after the "WITH" clause; and it is only available in the attached query that follows.

In the following examples, Note these details:

  1- The function needs to immediately follow the "WITH" keyword.
  2- The PL/SQL code that defines the function ends with a semicolon.
  3- Other "WITH" clause subqueries can follow the function declaration.
  4- The query can be terminated with a semicolon; but ...
  5- To run it in a SQL-Developer like tool, you may need a terminating slash "/".
  

The following is an example of a function that uses the 'calculator' like functionality of the "SELECT" to evaluate an expression. If the expression or 'numeric value' can be represented as a number, then that value is returned. If not, then the 'exception' handler in the function returns a "NULL". The 'table' being queried is built into the "WITH" clause in the example below.

   WITH
     FUNCTION to_nmbr(f_exp in varchar2) 
       return number 
     is
       v_num number ;
     BEGIN
       execute immediate 'select '||f_exp||' from dual' 
         into v_num ;
       return v_num ;
     exception  
       when others then return NULL ;
     END ;
   -----
     w_data  as
       ( select null as id, null as val from dual where 1=2
         ----------------------------------------------------
         union all select   1, '  218.95  '      from dual
         union all select   2, ' +56  '          from dual
         union all select   3, ' -84  '          from dual
         union all select   4, '   4.549e6   '   from dual
         union all select   5, ' +(((4.8)))  '   from dual
         union all select   6, ' 13+7+9-15  '    from dual
         union all select   7, '  40-2*(11-2) '  from dual
         union all select   8, '  -(123)  '      from dual
         union all select   9, ' -(5) - -(7)  '  from dual
         union all select  10,    NULL           from dual
         union all select  11, '  words  '       from dual
         union all select  12, ' a 721 '         from dual
         union all select  13, ' 2 3  '          from dual
         union all select  14, ' 3 - -2  '       from dual
         union all select  15, ' -321.7e-5  '    from dual
         ----------------------------------------------------
       )
   select  id,  val,  to_nmbr(val)  as rslt
     from  w_data 
    order  by 1 ;
   /


The following function tests a date value stored in a text string against a given format string. If the date evaluates against the given format, then the DATE value is returned; if not, then it returns "NULL". In the SQL query, the 'coalesce()' tests several format strings for each date; the first successfull one is returned as the 'best-guess' DATE value.

   WITH
     FUNCTION try_dt
       ( f_dt in varchar2
       , f_fmt in varchar2 
       )  return date is
       v_dt date ;
     begin
       v_dt := to_date( f_dt, f_fmt ) ;
       return v_dt ;
     exception  when others then return NULL ;
     end ;
   select  id
        ,  dt
        ,  coalesce
             ( try_dt(dt, 'YYYY-MM-DD') 
             , try_dt(dt, 'YYYY-Mon-DD') 
             , try_dt(dt, 'MM-DD-YYYY') 
             , try_dt(dt, 'DD-MM-YYYY') )  as best_dt_guess
     from  ( select null as id, null as dt from dual where 1=2
             -------------------------------------------------
             union all select  1, '2018-Oct-17' from dual
             union all select  2, '1949-01-05'  from dual
             union all select  3, '4/25-2004'   from dual
             union all select  4, '25/4/2018'   from dual
             union all select  5, '3-7-1984'    from dual
             union all select  6, '11.5.1978'   from dual
             union all select  7, '2:22:2021'   from dual
             union all select  8, '2020-2-30'   from dual
           )
    order  by 1 
   /


One last example. This query has a built in 'Multi-Replace' function, allowing a Replace() like functionality but with lists of search/replace values.

   
   WITH
   ---------- /* Function Definition */
     function  replace_mltpl
       ( f_txt    varchar2
       , f_srch   varchar2
       , f_rplc   varchar2
       , f_dlim   char       default ','  
       )
     RETURN  VARCHAR2
     AS
        v_str   varchar2(4000) ;
        v_cnt   integer        ;
        v_exp   varchar2(20)   ;
        v_sep   char           := f_dlim ;
     BEGIN
        v_str := f_txt ; 
        v_cnt := regexp_count(f_srch, '\'||v_sep)+1 ;  
        v_exp := '\'||v_sep||'?([^\'||v_sep||']*)' ;
        for n in 1..v_cnt loop
           v_str := regexp_replace
                      ( v_str
                      , regexp_substr(f_srch, v_exp, 1, n, 'i', 1)
                      , '®¤'||n||'¤®'
                      ) ;
        end loop ;
        for n in 1..v_cnt loop
           v_str := regexp_replace
                      ( v_str
                      , '®¤'||n||'¤®'
                      , regexp_substr(f_rplc, v_exp, 1, n, 'i', 1)
                      ) ;
        end loop ;
        RETURN  v_str ; 
     END ;
   ---------- /* Sample Data */
     w_data  as
       ( select  NULL  as txt
              ,  NULL  as srch
              ,  NULL  as rplc
              ,  NULL  as dlm
           from  dual  where 1=2
         union all select  'A quick brown fox jumps over the lazy dog.' 
                        , 'fox~dog~lazy~quick' , 'dog~fox~quick~lazy'       
                        , '~'  
                     from dual
         union all select  'A quick brown fox jumps over the lazy dog.' 
                        , 'fox~dog~lazy~quick' , 'walaby~dingo~mangy~agile' 
                        , '~'  
                     from dual
         union all select  'a~b=c; x-y~z; 2=3~4; e=f-g'                 
                        , '~|=|-|;| '          , '=|-|~| |/'                
                        , '|'  
                     from dual
         union all select  'one two three; four five six; seven 8 9'    
                        , '; , ,e'             , ': ,-,E'                   
                        , ','  
                     from dual
       )
   ---------- /* Query */
   select  txt, srch, rplc, dlm
        ,  replace_mltpl( txt, srch, rplc, dlm )  as rslt
     from  w_data 
   /

No comments:

Post a Comment