Oracle 动态查询在执行表函数之前重写或评估查询

Oracle 动态查询在执行表函数之前重写或评估查询,oracle,Oracle,首先,我想明确一点,问题不在于物化视图特性 假设我有一个表函数,它返回一组预定义的列 当函数调用作为 SELECT col1, col2, col3 FROM TABLE(my_tfn(:p1)) WHERE col4 = 'X'; 我可以计算参数并选择要执行的查询。 我可以打开一个预定义的游标,也可以动态组合查询 如果我不计算参数,而是计算请求查询的文本,该怎么办 例如,如果我的函数返回20列,但查询只请求4列, 我可以为返回类型的其余16个束分配空值,并执行更少的联接。 或者我可以将过滤器

首先,我想明确一点,问题不在于物化视图特性

假设我有一个表函数,它返回一组预定义的列

当函数调用作为

SELECT col1, col2, col3
FROM TABLE(my_tfn(:p1))
WHERE col4 = 'X';
我可以计算参数并选择要执行的查询。 我可以打开一个预定义的游标,也可以动态组合查询

如果我不计算参数,而是计算请求查询的文本,该怎么办

例如,如果我的函数返回20列,但查询只请求4列, 我可以为返回类型的其余16个束分配空值,并执行更少的联接。 或者我可以将过滤器向下推到我的动态查询

有没有办法做到这一点?
更一般地说,是否有一种方法可以在发出函数之前查看请求查询?

没有可靠的方法来识别调用PL/SQL对象的SQL

下面是一种识别调用SQL的不太可靠的方法。我以前使用过类似的代码,但只有在我知道PL/SQL永远不会并发运行的特殊情况下才使用

--Create simple test type for function.
create or replace type clob_table is table of clob;

--Table function that returns the SQL that called it.
--This requires elevated privileges to run.
--To simplify the code, run this as SYS:
--  "grant execute on sys.dbms_shared_pool to your_user;"
--(If you don't want to do that, convert this to invoker's rights and use dynamic SQL.)
create or replace function my_tfn return clob_table is
    v_my_type clob_table;
    type string_table is table of varchar2(4000);
    v_addresses string_table;
    v_hash_values string_table;

begin
    --Get calling SQL based on the SQL text.
    select sql_fulltext, address, hash_value
    bulk collect into v_my_type, v_addresses, v_hash_values
    from gv$sql
    --Make sure there is something unique in the query.
    where sql_fulltext like '%my_tfn%'
        --But don't include this query!
        --(Normally creating a quine is a challenge, but in V$SQL it's more of
        -- a challenge to avoid quines.) 
        and sql_fulltext not like '%quine%';

    --Flush the SQL statements immediately, so they won't show up in next run.
    for i in 1 .. v_addresses.count loop
        sys.dbms_shared_pool.purge(v_addresses(i)||', '||v_hash_values(i), 'C');
    end loop;

    --Return the SQL statement(s).
    return v_my_type;
end;
/
这看起来应该很简单。数据字典跟踪所有会话并运行SQL。您可以使用
sys\u context('userenv','sid')
查找当前会话,将其与
GV$session
匹配,然后获取
SQL\u ID
PREV\u SQL\u ID
。但它们都不包含正在调用的SQL。在
SYS\u CONTEXT
中甚至有
CURRENT\u SQL
,但它只用于细粒度审计

相反,调用SQL必须通过字符串搜索找到。为PL/SQL对象使用唯一的名称将有助于筛选出不相关的语句。为了防止重新运行旧语句,必须在找到SQL后立即从共享池中逐个清除SQL。这可能会导致竞争条件,因此此方法只有在从未同时调用时才有效

--Create simple test type for function.
create or replace type clob_table is table of clob;

--Table function that returns the SQL that called it.
--This requires elevated privileges to run.
--To simplify the code, run this as SYS:
--  "grant execute on sys.dbms_shared_pool to your_user;"
--(If you don't want to do that, convert this to invoker's rights and use dynamic SQL.)
create or replace function my_tfn return clob_table is
    v_my_type clob_table;
    type string_table is table of varchar2(4000);
    v_addresses string_table;
    v_hash_values string_table;

begin
    --Get calling SQL based on the SQL text.
    select sql_fulltext, address, hash_value
    bulk collect into v_my_type, v_addresses, v_hash_values
    from gv$sql
    --Make sure there is something unique in the query.
    where sql_fulltext like '%my_tfn%'
        --But don't include this query!
        --(Normally creating a quine is a challenge, but in V$SQL it's more of
        -- a challenge to avoid quines.) 
        and sql_fulltext not like '%quine%';

    --Flush the SQL statements immediately, so they won't show up in next run.
    for i in 1 .. v_addresses.count loop
        sys.dbms_shared_pool.purge(v_addresses(i)||', '||v_hash_values(i), 'C');
    end loop;

    --Return the SQL statement(s).
    return v_my_type;
end;
/
现在,这样的查询将自行返回,这表明PL/SQL代码正在读取调用它的SQL:

SELECT * FROM TABLE(my_tfn) where 1=1;
SELECT * FROM TABLE(my_tfn) where 2=2;

但是,即使你经历了所有这些麻烦,你会对结果做什么?解析SQL非常困难,除非你能确保每个人都遵守严格的语法规则。

我不想支持这种事情。如果查询位于存储过程中,则可以检查调用堆栈,并使用数据字典从代码中查找查询。您可能能够获取当前会话的sql_id并查找查询文本,但我不认为这在不同版本中都适用。如果你想改变行为,我认为让函数接受一个额外的参数会更有意义。如果你真的下定决心,你可以使用高级重写让任意查询实际执行一个完全不同的查询。但是,我非常明智地选择这样做,因为这让调试系统变得相当“有趣”。谢谢你,贾斯汀。这是非常有用的。唯一的问题是,我似乎必须提前知道请求查询是什么。希望通过某种方式查看该查询的文本。再次感谢。这太棒了!太谢谢你了。我该拿它怎么办?我正在研究不同的技术,我可以采用一个TFN,可以满足许多不同的要求。假设返回类型是一个扁平的数据集,它将许多相关的表连接在一起。但是,当只需要几个属性时,我希望避免不必要的工作。不知道我是否能运用你的技巧。并发性是一个问题。但这是我需要考虑的。这是针对我正在设计的数据虚拟化解决方案。