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

假设我触发了一个查询:

select MyProc(id) from tableName;

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

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

谢谢。

【问题讨论】:

    标签: function plsql


    【解决方案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:你说得对,这当然不是生产代码。我应该从粗体的免责声明开始。我的目的是为更强大的解决方案提供一个良好的起点。但实际上,避免这个问题可能比尝试解决它更好。这是这里的众多问题之一,乍一看似乎很合理,但当你仔细观察时,唯一完整的答案是“构建一种新的编程语言”。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-18
        • 1970-01-01
        相关资源
        最近更新 更多