如何使用jdbc从Oracle中的结果集中获取模式名称?

时间:2010-11-02 20:53:56

标签: database oracle jdbc metadata resultset

我想在Oracle数据库上运行查询,对于结果集中的每一列,我想知道该列来自哪个模式。我尝试了以下方法:

ResultSetMetaData rsMetadata = rs.getMetaData();
String schemaName = rsMetadata.getSchemaName(1)

但是,这会返回一个空字符串。是否有任何解决方案来获取模式名称?

编辑以回应OMG小马:

我们正在开发的工具从数据库中获取数据并分析数据,以找到给定问题的信息量最大的子集。然后,我们创建一个查询,该查询仅返回对给定问题提供信息的行。例如,如果我们有一个客户数据库并想知道哪些客户最有可能停止其服务,我们的工具可以创建一个查询,该查询返回5%的客户记录,然后可以通过高性能分析算法运行。好处是我们只对一部分数据进行分析,这当然会节省时间。事实证明,高功率分析算法现在工作得更好,因为第一步主要是从我们的数据中滤除噪声。

因此,为了响应OMG Ponies,用户指定数据库连接信息和查询作为我们工具的输入。因为他们可以指定他们喜欢的任何查询,所以用户可以连接到schema foo,然后运行以下查询:

SELECT* FROM bar.customer;

如果出于某种原因,眼睛的颜色和性别是人们停止服务的预测因素,那么我们系统生成的查询结果可能如下所示:

SELECT * FROM bar.customer WHERE bar.customer.eye_color='blue' 
                                 AND bar.customer.gender='M' 

了解结果集中每列的架构会很高兴,这样我们就可以确保我们的查询能够正确运行。我们可以假设模式与数据库连接中使用的模式相同,并且在99%的时间都应该没问题。我只关心1%的用户可能会做一些意想不到的事情,比如对另一个架构运行查询。

2 个答案:

答案 0 :(得分:2)

根据旧的Oracle code sample

  

OracleResultSetMetaData接口没有实现getSchemaName()和getTableName()方法,因为底层协议不能实现这一点。

这对我来说意味着ResultSetMetaData也不会为Oracle提供这些方法,至少在使用Oracle驱动程序时是这样。 (我尝试使用OCI驱动程序,看看是否有所作为,但显然没有。)

有一个WebLogic 8 document表明可以完成,但在以后的版本中已弃用该类型4驱动程序。因此,您可能仍然可以找到支持getSchemaName()针对Oracle的第三方驱动程序,但似乎不太可能。

答案 1 :(得分:0)

您可以通过一些自定义函数和查询获取此信息。基本上,使用DBMS_SQL查找查询使用的列,然后将其与v $ sql_plan中引用的表进行匹配。但是这种方法可能存在很多问题;在哪个列来自哪个表和解释计划中的不同对象之间可能存在歧义等等。

--#1: Create some test data
create table employee (id number primary key, name varchar2(100), department_id number);
create table department(id number primary key, name varchar2(100), test number);
insert into department select level, 'department test', level from dual connect by level <= 100;
insert into employee select level, 'employee test', level from dual connect by level <= 100;

--Actually run the query for this example so there will be data in the data dictionary.
select employee.* from employee inner join department on department_id = department.id;

--#2: The first difficult part is to find the sql_id of the query.  Can you get this directly from the
--    result set?  If not not you'll have to find it.
--    I'm not exactly sure how you'll want to do this, here are some options:
--Look at the last loaded query in v$sql (I don't think this always works, especially if the query has run multiple times)
select * from v$sql where v$sql.parsing_schema_name = user order by first_load_time desc;
--Compare the query text (sql_text removes newlines, sql_fulltext is a clob)
select * from v$sql where sql_text like 'select employee.* from employee inner join department on department_id = department.id%';
--Find the last sql_id for this session.  This doesn't work for me, maybe it's just an express edition bug?
select prev_sql_id, v$session.* from v$session where sid = sys_context('USERENV', 'SID');

--Look at the plan.  Note that there may be an index instead of a table.
--(On my system the sql_id is 0k2t2y1d312j8, but it will probably be different on yours)
select * from v$sql_plan where sql_id = '0k2t2y1d312j8';

--3: Create a type and a function to return all of the columns from a specific query.
--It'd be more consistent to use the SQL_ID here, but then there are permission issues if we
--have to get the text from v$sql.

create or replace type varchar2_tab is table of varchar2(30);
/

create or replace function get_columns(sql_text in varchar2) return varchar2_tab
authid current_user pipelined is
  my_cursor    number;
  column_count number;
  my_columns   DBMS_SQL.DESC_TAB;
begin
  select count(*) into column_count from v$sql;
  my_cursor := dbms_sql.open_cursor;
  dbms_sql.parse(my_cursor, sql_text, dbms_sql.native); 
  dbms_sql.describe_columns(my_cursor, column_count, my_columns);
  for i in 1 .. my_columns.count loop
    pipe row(my_columns(i).col_name);
  end loop;
  dbms_sql.close_cursor(my_cursor);
end;
/

--Test queries.  Note that it's possible for a column to be listed twice.
select * from table(get_columns('select employee.* from employee inner join department on department_id = department.id'));

--4: Find the columns and their tables and schemas that are used in a query.
--Currently this only works for tables and indexes in the explain plan.
--There's probably a large number of items that won't work - materialized views, clusters(?), pipelined functiions, etc.
--You'll need to add each object type as necessary.
--(Remember to replace the SQL_ID and the query text with the real values)
select distinct owner, table_name, column_name
from
(
  --Find all the columns for the relevant tables
  select all_tab_cols.owner, all_tab_cols.table_name, all_tab_cols.column_name
  from
  (
    --Find the relevant tables from the plans (may need to find the table behind an index)
    select
      nvl(all_indexes.table_owner, plan_objects.object_owner) owner,
      nvl(all_indexes.table_name, plan_objects.object_name) table_name
    from
    (
      select object_owner, object_name, object_type
      from v$sql_plan
      where sql_id = '0k2t2y1d312j8'
        and
        (
          object_type = 'TABLE'
          or object_type like 'INDEX%'
        )
    ) plan_objects
    left outer join all_indexes
      on plan_objects.object_name = all_indexes.index_name
        and plan_objects.object_type like 'INDEX%'
  ) relevant_tables
  inner join all_tab_cols
    on relevant_tables.owner = all_tab_cols.owner
      and relevant_tables.table_name = all_tab_cols.table_name
) relevant_tab_cols
--It would be more 
inner join table(get_columns('select employee.* from employee inner join department on department_id = department.id')) all_possible_columns
  on relevant_tab_cols.column_name = all_possible_columns.column_value;