PLS-00382: выражение имеет неправильный тип при заполнении таблицы ассоциативного массива

#plsql

Вопрос:

У меня есть такая структура таблицы в моей базе данных

введите описание изображения здесь

Теперь я хочу, чтобы все значения внутри него были в ассоциативном массиве, который индексируется типом job_id%, который является varchar2, поэтому я сначала создал этот анонимный блок, прежде чем создавать процедуру для его тестирования, и я хочу заполнить свой ассоциативный массив результатами:

 DECLARE
    TYPE jobs_tab_type IS TABLE OF jobs%rowtype INDEX BY jobs.job_id%type;
    jobstab jobs_tab_type;
BEGIN

    FOR rec IN (SELECT * FROM jobs)
    LOOP
        jobstab(rec.job_id) := rec.job_id;
    END LOOP;
END;
 

Я точно знаю, что это неправильный способ сделать это, поскольку я столкнулся с этой ошибкой: PLS-00382: expression is of wrong type , но в то же время я не знаю, есть ли какой-либо правильный способ сделать это. Я видел документацию, но во всех примерах использовалась определяемая пользователем строка, которая будет содержать job_id , но проблема в том, что мне нужно, чтобы job_id был проиндексирован, а не определяемая пользователем строка. Есть ли какой-либо способ исправить эту проблему в PL / SQL?

Ответ №1:

Вам не нужен индекс by INDEX BY jobs.job_id%type . Это делает ваш код более сложным. INDEX BY Определяет индекс коллекции — то есть символ, используемый для обозначения позиции в коллекции. Это не обязательно должен быть столбец из таблицы, из которой вы берете %rowtype . Обычно INDEX BY VARCHAR2 используется, если вы хотите ссылаться на коллекцию на основе значимой строки, например capitals('United States') = 'Washingston'

Теперь, что касается вашего вопроса. Вот ответ на первоначальный вопрос:

 
create table jobs
(job_id VARCHAR2(10)
,job_title VARCHAR2(30)
,min_salary NUMBER
,max_salary NUMBER
);

INSERT INTO jobs (job_id, job_title, min_salary, max_salary) VALUES ('a','CLERK',100,500);
INSERT INTO jobs (job_id, job_title, min_salary, max_salary) VALUES ('b','PRESIDENT',1000,5000);
INSERT INTO jobs (job_id, job_title, min_salary, max_salary) VALUES ('c','SALESMAN',500,800);

DECLARE
    TYPE jobs_tab_type IS TABLE OF jobs%rowtype INDEX BY jobs.job_id%type;
    jobstab jobs_tab_type;
    l_idx VARCHAR2(100);  
BEGIN

    FOR rec IN (SELECT * FROM jobs)
    LOOP
        jobstab(rec.job_id) := rec;
    END LOOP;
    
    l_idx := jobstab.FIRST;     
    WHILE (l_idx IS NOT NULL) LOOP
      dbms_output.put_line(l_idx ||': '||jobstab(l_idx).job_title);
      l_idx := jobstab.NEXT(l_idx);
    END LOOP;
END;
/

a: CLERK
b: PRESIDENT
c: SALESMAN

 

Обратите внимание, что в этом блоке немного утомительно перебирать элементы, просто потому, что индекс не является целым числом. Теперь давайте рассмотрим ту же функциональность, но с использованием INDEX BY BINARY_INTEGER :

 DECLARE
    TYPE jobs_tab_type IS TABLE OF jobs%rowtype INDEX BY BINARY_INTEGER;
    jobstab jobs_tab_type;
    l_idx NUMBER := 1;
BEGIN

-- example 1: using a cursor for loop

--    FOR rec IN (SELECT * FROM jobs)
--    LOOP
--        jobstab(l_idx) := rec;
--        l_idx := l_idx   1;
--    END LOOP;
    
-- example 2: using bulk collect
    SELECT * BULK COLLECT INTO jobstab FROM jobs;
    
    FOR r IN 1 .. jobstab.COUNT LOOP
       dbms_output.put_line(r ||': '||jobstab(r).job_title);
    END LOOP;
END;
/


1: CLERK
2: PRESIDENT
3: SALESMAN

 

Проще перебирать значения коллекции из-за числового индекса, и он также может быть неявно присвоен, используя BULK COLLECT оператор.

Ответ №2:

Значением вашего ассоциативного массива является вся запись. Следовательно, просто удалите .job_id из этой строки вашего кода.

 jobstab(rec.job_id) := rec.job_id;
 

Другими словами, строка должна быть

 jobstab(rec.job_id) := rec;
 

Обратитесь к этой скрипке db<>