我正在研究组织的内部工具,目前我有自动化需求。
示例输入查询:
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解析器可以帮助我查询查询中的表列表?
注意:这是一个基本示例,我的查询以多行运行并且更复杂。
答案 0 :(得分:2)
有趣的问题。
您可以使用DBMS_FGA
在PL / 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)