Oracle 10g - 从我正在执行的查询中获取表的列表

时间:2016-10-07 15:08:26

标签: oracle python-3.x oracle10g

我正在研究组织的内部工具,目前我有自动化需求。

示例输入查询:

Select name,userid,url,address_line_1 from user join address on 
user.user_id = address.user_id where userid = 'xxyy';

我需要的是表格列表

user
address

Oracle 10G中是否有内置方法可以从此查询中获取表列表?或者是否有一个python解析器可以帮助我查询查询中的表列表?

注意:这是一个基本示例,我的查询以多行运行并且更复杂。

3 个答案:

答案 0 :(得分:2)

有趣的问题。

您可以使用DBMS_FGA在PL / SQL中构建一个小型SQL分析器。这个想法是:

  • 自动修改输入SQL以使用具有FGA策略的表
  • 在那个FGA政策中,您将可以访问当前的SQL(无论如何它的第一个32K。这是一个限制......)
  • 使用当前SQL在当前SQL上构建一个抛弃视图
  • USER_DEPENDENCIES
  • 中读取丢弃视图的依赖关系
  • 放弃丢弃视图。

以下是一个如何运作的示例:

(我为在所有对象中添加我的名字而道歉;我与其他人共享此数据库。)

-- Tester
BEGIN
   matt_analysis_pkg.analyze_sql(p_sql => 
      'WITH oel AS ( SELECT * 
                     FROM oe_order_lines 
                     WHERE ship_from_org_id = 88 ) 
      SELECT oel.line_id, msi.segment1 
      FROM oel INNER JOIN mtl_system_items msi 
         ON msi.organization_id = 92 and msi.inventory_item_id = oel.inventory_item_id');
END;
/

Objects referenced by current SQL: 
APPS.MTL_SYSTEM_ITEMS (SYNONYM)
APPS.OE_ORDER_LINES (SYNONYM)

示例(下面)仅报告第一级依赖项。您可以递归使用DBA_DEPENDENCIES来获得更多深度。此外,此版本只写入DBMS_OUTPUT

正如其他人报道的那样,仅仅因为SQL依赖于对象并不意味着Oracle将在运行时实际访问该对象。不过,我认为这与你要求的非常接近。

另外,我注意到您使用Oracle 10g标记了您的问题。我认为我的解决方案中唯一不能在10g中工作的是我直接访问序列。您必须用SELECT ... INTO替换该部分才能获得当前序列值。

无论如何,这是它的源代码(Oracle 12c):

-- This table doesn't do anything other than have a FGA policy on it.
CREATE TABLE matt_analysis_tab ( dummy varchar2(1) );

INSERT INTO matt_analysis_tab (dummy) VALUES ('X');

-- Sequence so we can create unique view names, in case two people analyze at the same time.
CREATE SEQUENCE matt_analysis_view_s;

-- Package to do the work.
CREATE OR REPLACE PACKAGE matt_analysis_pkg IS
  PROCEDURE analyze_sql ( p_sql CLOB );
  PROCEDURE analyze_current_sql (schema_name VARCHAR2, table_name VARCHAR2, policy_name VARCHAR2);
END matt_analysis_pkg;
/

CREATE OR REPLACE PACKAGE BODY matt_analysis_pkg AS

  PROCEDURE analyze_sql (p_sql CLOB) IS
    l_modified_sql CLOB := 'WITH v1$ AS ( SELECT /*+ MATERIALIZE */ dummy FROM matt_analysis_tab ) SELECT v1$.dummy, v2$.* FROM v1$, ( ' || p_sql || ') v2$';
  BEGIN
    DBMS_OUTPUT.PUT_LINE('l_modified_sql := ' || l_modified_sql);
    EXECUTE IMMEDIATE l_modified_sql;
  END analyze_sql;


  PROCEDURE analyze_current_sql (schema_name VARCHAR2, table_name VARCHAR2, policy_name VARCHAR2) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    l_sql CLOB;    
    l_column_count          INTEGER;
    l_view_name VARCHAR2(30);
    l_view_columns   VARCHAR2(4000);
  BEGIN
    l_sql := SYS_CONTEXT ('userenv', 'CURRENT_SQL',4000) 
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL1',4000)
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL2',4000)
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL3',4000)
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL4',4000)
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL5',4000)
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL6',4000)
    || SYS_CONTEXT ('userenv', 'CURRENT_SQL7',4000)
    ;
    DBMS_OUTPUT.put_line ('Current SQL: ' || l_sql);
    DBMS_OUTPUT.put_line ('Current SQL length (calc): ' || length(l_sql));
    DBMS_OUTPUT.put_line ('Current SQL length (userenv): ' || SYS_CONTEXT('userenv','CURRENT_SQL_LENGTH'));

    -- Parse the SQL to get the column count
    DECLARE
  l_cursor                INTEGER;
  l_column_descriptions   SYS.DBMS_SQL.desc_tab;

  BEGIN
    l_cursor   := sys.DBMS_SQL.open_cursor;

  -- parse SQL
  sys.DBMS_SQL.parse (c => l_cursor, statement => l_sql, language_flag => sys.DBMS_SQL.native);

  -- Describe columns
  sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);

  sys.DBMS_SQL.close_cursor (l_cursor);

END;
  DBMS_OUTPUT.PUT_LINE('Column count = ' || l_column_count);

  -- Build view columns.  We need to do this because the column names in the SQL are not necessarily unique.    
  SELECT listagg('C' || lpad(rownum,4,'0'),',') within group ( order by rownum )
  INTO    l_view_columns 
  FROM   dual 
  CONNECT BY rownum <= l_column_count;

  DBMS_OUTPUT.PUT_LINE('l_view_columns = ' || l_view_columns);

  l_view_name := 'matt_analysis_view_' || lpad(matt_analysis_view_s.nextval,6,'0') || '$';
  DBMS_OUTPUT.PUT_LINE('l_view_name = ' || l_view_name);

  l_sql := 'CREATE OR REPLACE FORCE VIEW ' || l_view_name || ' (' || l_view_columns || ') AS ' || l_sql;

  EXECUTE IMMEDIATE l_sql;

  DBMS_OUTPUT.PUT_LINE('Objects referenced by current SQL: ');
  FOR r IN ( select referenced_owner || '.' || referenced_name || ' (' || referenced_type || ')' reference_info 
             from user_dependencies where name = upper(l_view_name)
             AND  referenced_name not like 'MATT_ANALYSIS%' ) LOOP
    DBMS_OUTPUT.PUT_LINE(r.reference_info);
  END LOOP;

  EXECUTE IMMEDIATE 'DROP VIEW ' || l_view_name;

  COMMIT;

  END analyze_current_sql;
END matt_analysis_pkg;
/

-- Create the FGA policy 
BEGIN
  DBMS_FGA.add_policy (
    object_schema     => NULL,  -- My current schema
    object_name       => 'MATT_ANALYSIS_TAB',
    policy_name       => 'MATT_ANALYSIS_POLICY',
    audit_condition   => NULL,
    audit_column      => NULL,
    handler_schema    => NULL,  -- My current schema
    handler_module    => 'matt_analysis_pkg.analyze_current_sql',
    enable            => TRUE);
END;
/

-- Script to drop the policy, just in case
--EXEC  DBMS_FGA.drop_policy (NULL, 'MATT_ANALYSIS_TAB', 'MATT_ANALYSIS_POLICY');

答案 1 :(得分:0)

我还在等待解决方案,但是,现在,我想出了一个快速而又脏的方法,使用以下python获取表名和别名。

    from copy import deepcopy

    import cx_Oracle


    ############################################################################
    ####################### Database connection instantiation###################
    ############################################################################

    #connObj = pyodbc.connect(connString,autocommit=True)

    qry = """
                Select name,userid,url,address_line_1 from user join address on 
user.user_id = address.user_id where userid = 'xxyy'
            """


    def getConn():
        connObj = cx_Oracle.connect('asap', 'sdfssa', cx_Oracle.makedsn('DBLDEV03', 1521, '23432'))
        return connObj

    def destroy(connObj):
        connObj.commit()
        connObj.close()

        del connObj
    #####################       Logic  ########################################
    import datetime

    def getTablesFromQuery(qry):
        listTables = []
        listAliases = []
        connObj = getConn()
        cursor = connObj.cursor()
        listSpaceItems = qry.split(" ")
        found =False
        for spaceItem in listSpaceItems:
            if spaceItem != '' and spaceItem!='\n':
                spaceItem = spaceItem.replace("\n",'')
                listCommaItems = spaceItem.split(",")
                if found == True:
                    ##### We are assuming that the next item is always alias, the sql query should follow that rule to
                    ##### get the aliases properly.
                    listAliases.append(spaceItem)
                    found = False

                for commaItem in listCommaItems:
                    if commaItem != '':
                        item = commaItem

                        if "." in commaItem:
                            item=commaItem.split(".")[1]


                        cursor.execute('select * from all_tables where table_name=\''+item.upper()+'\'')
                        res = cursor.fetchall()
                        if res is not None and res.__len__()>0:
                            listTables.append(commaItem)
                            found = True
        destroy(connObj)
        return listTables,listAliases

    try:
        listTables, listAliases = getTablesFromQuery(qry)
        for item in listTables:
            print(''+item)

    except:
        print('Exception..')


############################################################################
####################### close database connection###########################
############################################################################

答案 2 :(得分:0)

您可以创建一个包过程来接受SQL语句作为输入。它的作用是将SQL包装在CREATE VIEW中,然后分析结果视图的依赖关系。

这是源代码。

CREATE OR REPLACE PACKAGE matt_analysis_pkg IS
  PROCEDURE analyze_sql ( p_sql CLOB );
END matt_analysis_pkg;
/

CREATE OR REPLACE PACKAGE BODY matt_analysis_pkg AS

  PROCEDURE analyze_sql (p_sql CLOB) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    l_sql CLOB;    
    l_column_count          INTEGER;
    l_view_name VARCHAR2(30);
    l_view_columns   VARCHAR2(4000);
  BEGIN
    DBMS_OUTPUT.put_line ('Current SQL: ' || p_sql);

    -- Parse the SQL to get the column count
    DECLARE
  l_cursor                INTEGER;
  l_column_descriptions   SYS.DBMS_SQL.desc_tab;

  BEGIN
    l_cursor   := sys.DBMS_SQL.open_cursor;

  -- parse SQL
  sys.DBMS_SQL.parse (c => l_cursor, statement => p_sql, language_flag => sys.DBMS_SQL.native);

  -- Describe columns
  sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);

  sys.DBMS_SQL.close_cursor (l_cursor);

END;
  DBMS_OUTPUT.PUT_LINE('Column count = ' || l_column_count);

  -- Build view columns.  We need to do this because the column names in the SQL are not necessarily unique.    
  SELECT listagg('C' || lpad(rownum,4,'0'),',') within group ( order by rownum )
  INTO    l_view_columns 
  FROM   dual 
  CONNECT BY rownum <= l_column_count;

  DBMS_OUTPUT.PUT_LINE('l_view_columns = ' || l_view_columns);

  l_view_name := 'matt_analysis_view_' || lpad(matt_analysis_view_s.nextval,6,'0') || '$';
  DBMS_OUTPUT.PUT_LINE('l_view_name = ' || l_view_name);

  l_sql := 'CREATE OR REPLACE FORCE VIEW ' || l_view_name || ' (' || l_view_columns || ') AS ' || p_sql;

  EXECUTE IMMEDIATE l_sql;

  DBMS_OUTPUT.PUT_LINE('Objects referenced by current SQL: ');
  FOR r IN ( select referenced_owner || '.' || referenced_name || ' (' || referenced_type || ')' reference_info 
             from user_dependencies where name = upper(l_view_name)
             AND  referenced_name not like 'MATT_ANALYSIS%' ) LOOP
    DBMS_OUTPUT.PUT_LINE(r.reference_info);
  END LOOP;

  EXECUTE IMMEDIATE 'DROP VIEW ' || l_view_name;

  COMMIT;

  END analyze_sql;
END matt_analysis_pkg;
/

测试

BEGIN
   matt_analysis_pkg.analyze_sql(p_sql => 
      'WITH oel AS ( SELECT * 
                     FROM oe_order_lines 
                     WHERE ship_from_org_id = 88 ) 
      SELECT oel.line_id, msi.segment1 
      FROM oel INNER JOIN mtl_system_items msi 
         ON msi.organization_id = 92 and msi.inventory_item_id = oel.inventory_item_id');
END;

Objects referenced by current SQL: 
APPS.MTL_SYSTEM_ITEMS (SYNONYM)
APPS.OE_ORDER_LINES (SYNONYM)