Как получить имя схемы из набора результатов в Oracle, используя jdbc? - PullRequest
3 голосов
/ 02 ноября 2010

Я хочу выполнить запрос к базе данных Oracle, и для каждого столбца в наборе результатов я хочу знать схему, из которой поступил столбец. Я попробовал следующее:

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

Однако, это возвращает пустую строку. Есть ли способ получить имя схемы?

Редактировать в ответ на пони OMG:

Инструмент, который мы разрабатываем, берет данные из базы данных и анализирует данные, чтобы найти наиболее информативное подмножество для данного вопроса. Затем мы создаем запрос, который возвращает только те строки, которые являются информативными для данного вопроса. Например, если у нас была база данных клиентов и мы хотели узнать, какие клиенты, скорее всего, прекратят их обслуживание, наш инструмент может создать запрос, который возвращает 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% случаев пользователь может сделать что-то неожиданное, например, выполнить запрос к другой схеме.

Ответы [ 2 ]

2 голосов
/ 09 ноября 2010

Согласно старому образцу кода Oracle :

Интерфейс OracleResultSetMetaData не реализует методы getSchemaName () и getTableName (), поскольку базовый протокол не делает это возможным.

Для меня это означает, что ResultSetMetaData не будет использовать эти методы для Oracle, по крайней мере, при использовании драйвера Oracle. (Я пытался с драйвером OCI, чтобы увидеть, если это имеет значение, но, видимо, нет).

Существует документ WebLogic 8 , в котором предлагается, что это можно сделать, но этот драйвер типа 4 не рекомендуется в более поздних выпусках. Таким образом, возможно, вы все равно сможете найти сторонний драйвер, который поддерживает getSchemaName() против Oracle, но это маловероятно.

0 голосов
/ 10 ноября 2010

Вы можете получить эту информацию с помощью некоторых пользовательских функций и запросов. По сути, найдите столбцы, используемые запросом с 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;
...