从 PL/SQL 函数访问查询的 from 子句中的表名

Posted

技术标签:

【中文标题】从 PL/SQL 函数访问查询的 from 子句中的表名【英文标题】:Accessing the tablename in the from clause of a query from a PL/SQL function 【发布时间】:2011-06-04 15:20:44 【问题描述】:

假设我触发了一个查询:

select MyProc(id) from tableName;

有没有一种方法可以访问上述查询中 from 子句中使用的 tableName,从过程:MyProc()?

然后我就可以在 MyProc() 过程中动态使用“来自表”。

谢谢。

【问题讨论】:

【参考方案1】:

不,你不能那样做。您可以将表作为过程的参数发送并在过程中使用动态 SQL:

SELECT MyProc( id, 'tablename' ) FROM dual;

但即使这样也很尴尬,并且具有动态 SQL 的所有限制。为什么你的程序需要表名?你想做什么?

【讨论】:

【参考方案2】:

您必须使用动态构建的查询来执行您正在尝试的操作。尝试类似

strSQL  VARCHAR2(32767);
csr     SYS_REFCURSOR;
nVal1    NUMBER;
nVal2    NUMBER;
strVal3  VARCHAR2(2000);

strSQL := 'SELECT val1, val2, val3 FROM ' || tableName || ' WHERE whatever = somethingelse';

OPEN csr FOR strSQL;
FETCH csr INTO nVal1, nVal2, strVal3;
CLOSE csr;

分享和享受。

【讨论】:

【参考方案3】:
create or replace function myProc(p_id number) return varchar2 is
    v_sql_id varchar2(13);
    v_table_name varchar2(100);
begin
    --Get the SQL used to call this function
    select sql_id into v_sql_id
    from v$sql
    where lower(sql_text) like 'select myproc(id)%'
        and users_executing > 0;

    --Get the table name
    select object_name into v_table_name
    from v$sql_plan
    where sql_id = v_sql_id
        and operation = 'TABLE ACCESS';

    --For testing, return the table name.
    return v_table_name;
end;
/

create table test1(id number);
create table test2(id number);
insert into test1 values(1);
insert into test2 values(2);
commit;

--Returns TEST1 (careful, your IDE may add this comment to the SQL!)
select MyProc(id) from test1;

--Returns TEST2
select MyProc(id) from test2;

这里的思路是找到当前正在执行的SQL,然后找到那个SQL使用的表。但是有很多潜在的问题。

获取 SQL_ID

有很多方法可以找到 SQL_ID,但没有一种方法能很好地工作。这是我尝试过的方法,也许有人可以弄清楚如何使其中一种更好地工作。

例如,在 v$session 中,SQL_ID 将引用自身,而 PREV_SQL_ID 引用一些无用的事务查询(至少在我的系统上)。

select sql_id, prev_sql_id from v$session where sid = sys_context('USERENV', 'SID');

在 v$sql 中查找查询并排序 LAST_LOAD_TIME 并不总是有效,LAST_LOAD_TIME 并不总是更新。

select sql_id from v$sql
where lower(sql_text) like 'select myproc(id)%'
order by last_load_time desc;

使用 SQL_TEXT 和 USERS_EXECUTING > 0 将起作用,但前提是一次只有一个会话正在执行此查询。搜索这样的文本是非常危险的。某些环境可能会在选择之前放置文本,例如空格或 cmets。但是您不能搜索“%select...”,因为这样查询会自行返回。

select sql_id into v_sql_id
from v$sql
where lower(sql_text) like 'select myproc(id)%'
    and users_executing > 0;

查找表

使用 SQL_ID,我们可以轻松地从 v$sql.sql_text 或 v$sql.sql_fulltext 获取查询文本。 可能您可以解析该查询,但总的来说,我建议您避免解析 SQL。这比大多数人想象的要困难得多。如果您绝对确定只会使用特定的简单查询,那么这种方法可能会奏效。

更现实的方法可能是使用 v$sql_plan 来查找使用的表。这将适用于您的查询,但如果您的查询可以有多个表,或者如果有视图或索引(您必须加入 user_index 以查找实际表),您将不得不做更多的工作,等等

select object_name
from v$sql_plan
where sql_id = <SQL_ID>
    and operation = 'TABLE ACCESS'

您可能需要将 v_$sql 和 v_$sql_plan 上的选择权限授予用户。哦,这会很慢。 @Eaolson 将表名作为参数传递的想法会好得多,如果可行的话。

【讨论】:

我会解雇所有编写此类代码的人,即使它显示了一些深刻的技术洞察力。这是在开发人员机器上运行的代码的主要示例。但是,一旦置于现实的需求环境中,它可能会很慢,并且 - 正如您所提到的 - 如果多个人同时执行它会导致难以发现错误并可能导致安全问题。 @Codo:你说得对,这当然不是生产代码。我应该从粗体的免责声明开始。我的目的是为更强大的解决方案提供一个良好的起点。但实际上,避免这个问题可能比尝试解决它更好。这是这里的众多问题之一,乍一看似乎很合理,但当你仔细观察时,唯一完整的答案是“构建一种新的编程语言”。

以上是关于从 PL/SQL 函数访问查询的 from 子句中的表名的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估

PL/SQL学习

PL/SQL - 作为表名的函数参数 - 这怎么可能?

访问中的 from 子句中的语法错误

在 pl/sql 函数中选择子句返回错误值

PL/SQL查询中的空值问题