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?
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
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;
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..