Во-первых, ваши типы не совсем правильно объявлены.
В вашем типе T_REC
необходимо указать размер столбца VARCHAR2
. Я использовал 100
в качестве примера здесь:
TYPE T_REC IS RECORD
(
T_TITLE VARCHAR2(100),
T_YEAR NUMBER(2,1)
);
Во-вторых, строка
TYPE T_EMP IS TABLE OF T_REC%TYPE;
неверна: T_REC
сам по себе является типом, поэтому вы не указываете атрибут %TYPE
для него. Вместо этого попробуйте следующее:
TYPE T_EMP IS TABLE OF T_REC;
Есть также некоторые проблемы с тем, как вы определили свою функцию:
FUNCTION HIST(V_EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE)
RETURN V_EMP;
BEGIN
-- ...
Предложение RETURN
требует использования типа, но V_EMP
является локальной переменной. Кроме того, вместо завершения объявления точкой с запятой необходимо включить ключевое слово IS
, чтобы сообщить компилятору PL / SQL, что следующий блок формирует тело функции. Объединяя эти изменения, мы имеем:
FUNCTION HIST(V_EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE)
RETURN T_EMP
IS
BEGIN
-- ...
После исправления этих проблем с объявлениями мы можем посмотреть на блок BEGIN
внизу. Первая проблема заключается в том, что вы не можете написать SELECT T_TITLE, T_YEAR FROM Z_EMP
для запроса из переменной, которая содержит вложенную таблицу. Вместо этого вы должны обернуть его в вызов TABLE
, то есть SELECT T_TITLE, T_YEAR FROM TABLE(Z_EMP)
.
Однако это не сработает. Вы получите ошибку PLS-00642: local collection types not allowed in SQL statements
, если попытаетесь. Это потому, что вы не можете выполнить запрос SQL для типов, объявленных только в блоке PL / SQL. Вместо этого вы можете l oop над значениями в возвращенной коллекции, используя следующее:
IF Z_EMP.COUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('There are no records');
ELSE
FOR i IN Z_EMP.FIRST .. Z_EMP.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(Z_EMP(i).T_TITLE || ', ' || Z_EMP(i).T_YEAR);
END LOOP;
END IF;
Обратите внимание, что в этом случае нам нужно проверить, что коллекция не имеет записей: если коллекция пуста, Z_EMP.FIRST
и Z_EMP.LAST
будут NULL
, и вы получите PL/SQL: numeric or value error
, пытаясь использовать их в диапазоне FOR
l oop. Также обратите внимание, что DBMS_OUTPUT.PUT_LINE
принимает только один аргумент: чтобы избежать ошибки, я объединил два значения вместе с запятой между ними.
В качестве альтернативы, если вы действительно хотите использовать запрос SQL для чтения Значения, возвращаемые вашей функцией, у вас немного больше работы. Вам нужно будет объявить типы T_REC
и T_EMP
вне вашего блока PL / SQL следующим образом:
CREATE TYPE T_REC IS OBJECT
(
T_TITLE VARCHAR2(100 CHAR),
T_YEAR NUMBER(2,1)
);
/
CREATE TYPE T_EMP IS TABLE OF T_REC;
/
Затем вы удалите объявление этих типов в своем блоке. Вам также необходимо настроить запрос внутри вашей функции: вместо выбора
SELECT JOB_TITLE T_TITLE, ROUND((END_DATE - START_DATE) / 365,1) T_YEAR
и сопоставления этих полей с записями вам придется явно создать объект T_REC
из каждой выбранной строки:
SELECT T_REC(JOB_TITLE, ROUND((END_DATE - START_DATE) / 365,1))
Как только вы это сделаете, l oop внизу можно изменить на следующее:
FOR C IN (SELECT T_TITLE, T_YEAR FROM TABLE(Z_EMP))
LOOP
DBMS_OUTPUT.PUT_LINE(C.T_TITLE || ', ' || C.T_YEAR);
END LOOP;
Вы также можете избавиться от чека Z_EMP.COUNT = 0
, если вы хочу: вышеприведенный l oop не сообщит об ошибке, если Z_EMP
пуст, хотя он не будет генерировать никакого вывода.