PL/SQL: How do I declare session variables?

前端 未结 3 527
眼角桃花
眼角桃花 2021-02-04 05:18

How do I declare a session variable in PL/SQL - one that will persist for the duration of the session only, without me having to store it in the database itself?

相关标签:
3条回答
  • 2021-02-04 05:27

    You can use a 'user-created context' to store data shared across multiple units within a session.

    First, create a context:

    CREATE CONTEXT SYS_CONTEXT ('userenv', 'current_schema')|| '_ctx' USING PKG_COMMON
    

    Second, create a package that would manage your context:

    CREATE OR REPLACE PACKAGE PKG_COMMON
    IS
       common_ctx_name   CONSTANT VARCHAR2 (60)
                     := SYS_CONTEXT ('userenv', 'current_schema')
                        || '_ctx';
    
       FUNCTION fcn_get_context_name RETURN VARCHAR2;
       PROCEDURE prc_set_context_value (var_name VARCHAR2, var_value NUMBER);
    END;
    
    CREATE OR REPLACE PACKAGE BODY PKG_COMMON
    IS
       FUNCTION fcn_get_context_name
          RETURN VARCHAR2
       IS
       BEGIN
          RETURN common_ctx_name;
       END;
    
       PROCEDURE prc_set_context_value (var_name VARCHAR2, var_value NUMBER)
       IS
       BEGIN
          DBMS_SESSION.set_context (common_ctx_name, var_name, var_value);
       END;
    END;
    

    The prc_set_context_value can be more advanced, that's just an example. With the context and the package created you can start using them. Set a context variable using a procedure call

    begin
      PKG_COMMON.prc_set_context_value('MyVariable', 9000)
    end;
    

    and use it anywhere - any procedure, package, function or event a view.

    CREATE VIEW V_TEST AS
      SELECT ID, LOGIN, NAME 
        FROM USERS 
       WHERE ROLE_ID =  SYS_CONTEXT(PKG_COMMON.FCN_GET_CONTEXT_NAME, 'MyVariable')
    

    For more information see http://www.psoug.org/reference/sys_context.html

    0 讨论(0)
  • 2021-02-04 05:27

    You create a package level variable. This is a minimal example:

    CREATE OR REPLACE PACKAGE my_package
    AS
        FUNCTION get_a RETURN NUMBER;
    END my_package;
    /
    
    CREATE OR REPLACE PACKAGE BODY my_package
    AS
        a  NUMBER(20);
    
        FUNCTION get_a
        RETURN NUMBER
        IS
        BEGIN
          RETURN a;
        END get_a;
    END my_package;
    /
    

    If you do this you should read up on (and handle correctly) ORA-04068 errors. Each database session will have it's own value for a. You can try this with:

    SELECT my_package.get_a FROM DUAL;
    
    0 讨论(0)
  • 2021-02-04 05:36

    I like to use a short, but intuitive syntax and thus would create e.g. some ctx package that just provides one function to set and get some global "variables".
    (valid for the current session only ; in my case no need to implement it as user-created-context variables, but easily changeable to it under the hood ; example with some foo varchar2 and bar number vars)

    usage:

    select ctx.foo from dual                                                 -- => null (init)
    select ctx.foo('a') from dual                                            -- => 'a'
    select ctx.foo('b') from dual ; select ctx.foo from dual                 -- => 'b', 'b'
    

    .

    -- (optimizer should cause the subquerys unselected columns not to be executed:)
    select 'ups' from (select ctx.foo('a') from dual) ; select ctx.foo from dual   -- => null
    
    select ctx.bar(1.5) from dual ; select ctx.bar from dual                 -- => 1.5,  1.5
    -- ...
    

    package header:

    create or replace package  ctx  as
    
      -- select ctx.foo from dual                                            -- => null (init)
      -- select ctx.foo('a') from dual                                       -- => 'a'
      -- select ctx.foo('b') from dual ; select ctx.foo from dual            -- => 'b', 'b'
      -- (optimizer should cause the subquerys unselected columns not to be executed:)
      -- select 'ups' from (select ctx.foo('a') from dual) ; select ctx.foo from dual
        -- => null
      -- parallel_enable for queries since it should not change inside of them
      function foo(  set varchar2 := null  ) return varchar2  parallel_enable;
    
      -- (samples like in foo above as executable test comments like in foo above skipped for 
      -- brevity)
      function bar(  set number := null  ) return  number  parallel_enable;
    
    end;
    

    package body:

    create or replace package body  ctx  as
    
      foo_  varchar2(30);  -- e.g. 'blabla'
      bar_  number;
    
    
      -- internal helper function for varchars
      function set_if_not_null( ref  in out  varchar2,  val  varchar2  ) return varchar2 as 
      begin
        if  val is not null  then  ref := val;  end if;
        return ref ;
      end;
    
    
      -- internal helper function for numbers
      function set_if_not_null( ref  in out  number,  val number  ) return  number  as begin
        if  val is not null  then  ref := val;  end if;
        return ref ;
      end;
    
    
      -- (same test comments like in foo above skipped for brevity)      
      function foo(  set varchar2 := null  ) return varchar2  parallel_enable as begin
        return set_if_not_null(  foo_,  set  ) ;
      end;
    
    
      -- (same test comments like in foo above skipped for brevity)      
      function bar(  set number := null  ) return  number  parallel_enable as begin
        return set_if_not_null(  bar_,  set  ) ;
      end;
    
    end;
    

    If you know that the variable (foo) may change within a single query then remove parallel_enable, which should otherwise be more performant if queries are parallelizable.

    Depending on ones needs one could of course add some foo_reset() to set it to null etc..

    0 讨论(0)
提交回复
热议问题