Поиск типа данных курсора или столбца таблицы в блоке

Можно ли узнать тип данных столбца курсора или переменной внутри блока без использования системных таблиц? Хотя я понимаю, что могу использовать системные таблицы для получения этой информации, это будет намного медленнее.

Что-то типа,

declare
   my_column_data_type varchar2(30);
begin
  my_column_data_type := all_tables.table_name%type;
  dbms_output.put_line(my_column_data_type);
end;

Я не могу найти способ сделать это, не прибегая к dbms_sql, что было бы излишним для моей конечной цели.

Но у Oracle уже есть вся информация. Если бы я попытался присвоить varchar2 number, то он сразу же пожаловался бы, чтобы знать, какие типы данных.

И да, я знаю, что количество версий Oracle смехотворно, но это количество, которое у нас есть на данный момент... 9i скоро умирает в пользу 11, но этот код сразу же будет работать на 9i, если я найду ответ ! Но я включил 11, так как могу дождаться лучшего решения, если понадобится,


person Ben    schedule 09.01.2012    source источник
comment
Не уверен, откуда берется ценность. Вы знаете имя столбца, так насколько сложно было бы также знать тип данных столбца?   -  person APC    schedule 09.01.2012
comment
если я неправильно понимаю вопрос, разве это не то, для чего предназначен% type? Таким образом, ваше объявление локальной переменной будет выглядеть так: l_var my_table.my_column%type; Теперь, когда я выбираю my_column в l_var из my_table, где... у меня нет проблем, будет тот же тип   -  person tbone    schedule 09.01.2012
comment
@tbone, да. Я хочу найти фактический тип данных; не создавать другую переменную того же типа. Звучит странно, я знаю, но мне нужно делать разные вещи в зависимости от того, является ли это символом или числом, и это чрезвычайно упростило бы изменение пакета (мне едва ли пришлось бы это делать).   -  person Ben    schedule 09.01.2012
comment
+1 за tbone .. Не могу не согласиться. Используйте %type для всех переменных, и тогда вам не нужно заботиться. В качестве дополнительного преимущества, когда столбец изменяется с VARCHAR (30) на VARCHAR (50), ваш код автоматически настраивается вместо того, чтобы выдавать слишком маленькую ошибку буфера символьной строки, когда он сталкивается с новыми более длинными данными.   -  person Craig    schedule 09.01.2012
comment
@Ben - Если вы просто хотите узнать, является ли значение числом или Varchar, может быть проще проверить, действительно ли значение является числом. Что-то вроде: IF translate(v_column_data, 'a0123456789', 'a') is null THEN /* числовая логика / ELSE / varchar логика */ END IF;   -  person Craig    schedule 09.01.2012
comment
@Craig, я использую %type для всех переменных; отсюда следует, что это %rowtype курсора. В любом случае использование %type не возвращает тип данных этого конкретного типа. Вы, конечно, правы насчет тестирования; Я бы попытался преобразовать в число и поймать исключение, но мне было интересно, есть ли встроенный способ сделать это, поскольку Oracle знает тип данных, так почему я должен проверять его снова?   -  person Ben    schedule 09.01.2012
comment
@Ben, можете ли вы привести пример, когда вы не знаете тип во время кодирования своей процедуры/функции? Возможно, вы используете слабо типизированный sys_refcursor? (а затем хотите строго типизировать, когда вы его используете?). Пожалуйста, дайте более подробную информацию в виде кода   -  person tbone    schedule 09.01.2012
comment
@tbone; У меня его нет. Это, прежде всего, чистый интерес. Я не мог найти никаких ссылок в Интернете, и никто на работе не знал. Оба довольно необычные. Что менее важно, я объединяю несколько пакетов в один, и я хотел сделать их как можно менее обслуживаемыми и бесфункциональными, и у меня была такая идея. Не уверен, что это сработает... но я не могу реализовать идею, пока не узнаю, что это возможно.   -  person Ben    schedule 09.01.2012


Ответы (2)


Звучит так, как будто вам нужен самоописывающий объект. Значение программно найти тип переменной без выбора из какого-либо представления метаданных. Просто спросите объект, кто вы?

В большинстве ситуаций это кажется ненужным, поскольку в большинстве случаев мы уже знаем тип (строго типизированный). Например, параметры процедуры обычно указывают тип (число, varchar2 и т. д.). Локальные переменные обычно указывают тип или привязываются к типу объекта базы данных через нотацию %type.

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

create or replace procedure get_data(o_cur OUT SYS_REFCURSOR) as
begin
  OPEN o_cur FOR
  -- without changing parameter, this could select from any table
  select * from emp;
end;

Теперь проблема в том, что у вас могут возникнуть ошибки (во время выполнения), если кто-то запрограммирует курсор для использования с другой таблицей (я намеренно выбрал ужасное имя процедуры). Что-то типа:

declare
  l_cur sys_refcursor;
  l_row dept%rowtype;
begin
  get_data(l_cur);
  -- oops, I thought this was dept data when I coded it, Oracle didn't complain at compile time
  LOOP
    fetch l_cur
    into l_row;
    exit when l_cur%notfound;
    -- do something here
  END LOOP;
  close l_cur;
end;

По этой же причине я предпочитаю строго типизированные курсоры и избегаю такой ситуации.

В любом случае, в случае объекта с самоописанием вы можете использовать встроенный тип SYS.ANYDATA (аналогично SYS.ANYDATASET для универсальных типов коллекций). Я думаю, это было введено с 9i. Например, эта процедура принимает некоторые данные и выполняет логику ветвления в зависимости от типа:

CREATE OR REPLACE procedure doStuffBasedOnType(i_data in sys.anydata) is
  l_type         SYS.ANYTYPE;
  l_typecode     PLS_INTEGER;
begin
  -- test type
  l_typecode := i_data.GetType (l_type);

  CASE l_typecode
  when Dbms_Types.Typecode_NUMBER then
    -- do something with number
    dbms_output.put_line('You gave me a number');

  when  Dbms_Types.TYPECODE_DATE then
    -- do something with date
    dbms_output.put_line('You gave me a date');

  when  Dbms_Types.TYPECODE_VARCHAR2 then
    -- do something with varchar2
    dbms_output.put_line('You gave me a varchar2');

  else
    -- didn't code for this type...
    dbms_output.put_line('wtf?');

  end case;
end;

Здесь у вас есть ваше программное ветвление на основе типа. И использовать его:

declare
  l_data sys.anydata;
begin
  l_data := sys.anydata.convertvarchar2('Heres a string');
  doStuffBasedOnType(l_data);
end;

-- output: "You gave me a varchar2"

Надеюсь, это был не слишком длинный ответ;)

person tbone    schedule 10.01.2012
comment
Вовсе нет, вы просто немного почитали sys_refcursors :-), что мне тоже не особо нравится, если честно :-). - person Ben; 10.01.2012
comment
правда, так иногда выходит. Рад, что это помогло. - person tbone; 10.01.2012

Используйте функцию дампа и сравните результат с этот код.

DUMP возвращает значение VARCHAR2, содержащее код типа данных, длину в байтах и ​​внутреннее представление expr.

person Sathyajith Bhat    schedule 09.01.2012
comment
+1 это действительно находка. Один момент для будущих пользователей, это можно вызвать только в select, поэтому вам нужно select dump(...,...) into ... в блоке PL\SQL. Результат отвратительный, поэтому я подожду, чтобы увидеть, не придумает ли кто-нибудь что-нибудь получше :-) - person Ben; 09.01.2012
comment
@Ben, это функция PL/SQL, вам не нужно помещать ее в оператор SELECT. - person Sathyajith Bhat; 09.01.2012
comment
если вы не используете его в операторе select, он вызывает PLS-00204. - person Ben; 09.01.2012
comment
@Бен упс - мой плохой! Хотя я использовал его как отдельную функцию - person Sathyajith Bhat; 09.01.2012
comment
Хорошая идея. Но будьте осторожны, в документации не перечислены все возможные коды типов данных. Например, SYSDATE и SYSTIMESTAMP на самом деле являются разными типами данных, кодами 13 и 188. Я не уверен, есть ли другие, не указанные в списке. - person Jon Heller; 10.01.2012