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

Posted

技术标签:

【中文标题】如何使用 jdbc 从 Oracle 中的结果集中获取模式名称?【英文标题】:How do I get the schema name from a resultset in Oracle using jdbc? 【发布时间】:2010-11-02 20:53:56 【问题描述】:

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

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

但是,这会返回一个空字符串。有什么办法可以获取架构名称吗?

编辑以回应 OMG Ponies:

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

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

SELECT* FROM bar.customer;

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

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

最好知道结果集中每一列的架构,这样我们就可以确保我们的查询能够正确运行。我们可以假设架构与数据库连接中使用的架构相同,并且在 99% 的情况下应该没问题。我只是担心有 1% 的时间用户可能会做一些意想不到的事情,比如针对另一个架构运行查询。

【问题讨论】:

只需要查看表即可获取架构,但如果您正在处理视图,那会让您感到悲痛......它引出了一个问题 - 为什么 don 't 您有权访问架构吗? 好问题!简短的回答是用户可以指定他们喜欢的任何查询,这意味着他们理论上可以引用任何模式。我用更完整的答案编辑了问题。 查询操纵器不能验证模式和表是否有效?听起来您正在执行修改后的查询并将结果传回,所以您已经知道它有效。如果您实际上将修改后的查询传回以供稍后执行,并且真的想要信息,您能否提取并传回所涉及的模式/表的列表?我仍然不清楚为什么会出现问题,除非您在以不同用户身份连接时修改和执行查询。 【参考方案1】:

根据老Oracle code sample:

OracleResultSetMetaData 接口没有实现 getSchemaName() 和 getTableName() 方法,因为底层协议无法实现。

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

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

【讨论】:

令人惊讶的是,10 年后,情况仍然如此:docs.oracle.com/en/database/oracle/oracle-database/19/jjdbc/…【参考方案2】:

您可以通过一些自定义函数和查询来获取此信息。基本上,使用 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;

【讨论】:

以上是关于如何使用 jdbc 从 Oracle 中的结果集中获取模式名称?的主要内容,如果未能解决你的问题,请参考以下文章

JDBC:如何从结果集中检索 SQL COUNT 函数的结果?

从 Oracle 中的动态 SQL 获取结果集中的结果

JDBC结果集

JDBC 从选择结果集中选择

如何从 Java JDBC 中的 callableStatement 获取滚动不敏感的结果集?

如何从 Oracle 中的 JDBC 批量插入中获取生成的密钥?