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

时间:2016-08-25 02:50:08

标签: oracle

首先,我想说清楚问题与物化视图功能无关。

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

当函数调用提交为

SELECT col1, col2, col3
FROM TABLE(my_tfn(:p1))
WHERE col4 = 'X';

我可以评估参数并选择要执行的查询。 我可以打开一个预定义的游标,也可以动态组合我的查询。

如果不是评估参数,而是要评估请求查询的文本,该怎么办?

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

有没有办法让这种情况发生? 更一般地说,有没有办法在退出函数之前查看请求查询?

1 个答案:

答案 0 :(得分:2)

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

下面是一种识别调用SQL的不太强大的方法。我之前使用过这样的代码,但只有在我知道PL / SQL永远不会同时运行的特殊情况下。

这个似乎就像它应该如此简单。数据字典跟踪所有会话并运行SQL。您可以使用sys_context('userenv', 'sid')找到当前会话,将其与GV$SESSION相匹配,然后获取SQL_IDPREV_SQL_ID。但这些都不包含调用SQL。 CURRENT_SQL中只有SYS_CONTEXT,但它只适用于细粒度的审核。

相反,必须通过字符串搜索找到调用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是非常困难的。