#sql #oracle #loops #plsql
Вопрос:
У меня есть курсор, который извлекает даты, которые мне нужно отфильтровать, затем цикл, в котором я получаю значения, и одно обновление с этими переменными
DECLARE
TYPE two_cols_rt IS RECORD
(
objectid_new BIDDATA.BID_GEN.OBJECTID%TYPE,
bidid_new BIDDATA.BID_GEN.BIDID%TYPE
);
CURSOR c IS
SELECT x.column_value startdate, x.column_value 6/24 enddate
FROM TABLE(
prod.date_utils(i_startdate => date '2015-4-21'
,i_enddate => date '2015-4-21'
,i_date_interval => 'HH')) x
WHERE MOD( TO_NUMBER( TO_CHAR(x.column_value, 'hh24') ), 6 ) = 0;
TYPE l_objects_t IS TABLE OF two_cols_rt;
l_objects l_objects_t;
BEGIN
FOR rec IN c
LOOP
SELECT (SELECT prod.admin.find('UNIT'
,'unit'
,x.res_name
,TRUNC(sysdate, 'dd')
,'N')
FROM dual) objectid_new
,x.BIDID
BULK COLLECT INTO l_objects
FROM biddata.bid_gen x
WHERE sced_time_stamp BETWEEN rec.startdate AND rec.enddate;
UPDATE BIDDATA.BID_GEN set objectid=l_objects.objectid_new
WHERE bidid=l_objects.bidid_new;
END LOOP;
END;
Но покажите мне эту ошибку:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 17
ORA-06512: at line 17
Мне не нужно возвращаться, мне просто нужно обновить одну строку для всех идентификаторов, которые получают первый выбор
С уважением
Комментарии:
1. Ошибка, по-видимому, указывает
select
на то, что заполняетl_objectID_new
иl_bidID
возвращает несколько строк. Если он возвращает несколько строк, что именно вы хотите сделать? Я предполагаю, что вы хотите определить эти локальные переменные как коллекции, а не скаляры, выполнитьbulk collect into
их, а затем выполнитьforall
цикл для повторения коллекции для обновления нескольких строкbid_gen_rt_ercot
. Или избавьтесь отselect
локальных переменных и сделайтеupdate
это напрямую.2. привет, Джастин, это правда, но я изучаю Oracle, я никогда раньше не использовал массовый сбор.
3. Это вопрос для домашнего задания? Если да, то какие конструкции вам разрешено/ необходимо использовать? Лично я бы написал это как один
update
оператор, а не использовал циклselect
, отдельные или любые локальные переменные. Но я не знаю, будет ли это разумным решением для вас.4. Привет, да, я попробовал просто обновить, но поставил мне, что select нужно поместить в оператор предложения. как вы это делаете?
5. Я просто использовал массовый сбор в, я обновил код, но показал мне эту ошибку: ORA-06550: строка 34, столбец 31: PLS-00302: компонент ‘BIDID_NEW’ должен быть объявлен ORA-06550: строка 34, столбец 21: PL/SQL: ORA-00904: «L_OBJECTS».»BIDID_NEW»: недопустимый идентификатор ORA-06550: строка 33, столбец 9: PL/SQL: Оператор SQL игнорируется
Ответ №1:
Если вы хотите сохранить цикл, вы можете обновить несколько строк
UPDATE BIDDATA.BID_GEN_RT_ERCOT x
set objectid=prod.object_detail_admin.find('ERCOT_UNIT'
,'ERCOT'
,x.res_name
,trunc(sysdate, 'dd')
,'N')
WHERE x.sced_time_stamp between rec.startdate and rec.enddate;
Вы могли бы полностью избавиться от цикла и просто сделать
UPDATE BIDDATA.BID_GEN_RT_ERCOT
set objectid=prod.object_detail_admin.find('ERCOT_UNIT'
,'ERCOT'
,res_name
,trunc(sysdate, 'dd')
,'N')
WHERE exists( select 1
from table(
prod.date_utils.get_listofdates_(i_startdate => date '2015-4-21'
,i_enddate => date '2015-4-21'
,i_date_interval => 'HH')) lod
where mod( to_number( to_char(lod.column_value, 'hh24') ), 6 ) = 0
and x.sced_time_stamp between lod.column_value
and lod.column_value 6/24 );
Если вы хотите сохранить цикл, локальную переменную и отдельные select
update
операторы и, вам нужно будет выполнить итерацию по коллекции, чтобы выполнить update
forall i in 1 .. l_objects.count
update BIDDATA.BID_GEN_RT_ERCOT
set objectid=l_objects(i).objectid_new
WHERE bidid=l_objects(i).bidid_new;
Комментарии:
1. Привет, Джастин, я только что обновил код из поста, используя МАССОВЫЙ СБОР, но покажите мне эту ошибку: ORA-06550: строка 34, столбец 31: PLS-00302: компонент ‘BIDID_NEW’ должен быть объявлен ORA-06550: строка 34, столбец 21: PL/SQL: ORA-00904: «L_OBJECTS».»BIDID_NEW»: недопустимый идентификатор ORA-06550: строка 33, столбец 9: PL/SQL: Оператор SQL игнорируется. есть идеи?
2. @Webb — я добавил третий вариант. Это будет наименее эффективным вариантом и потребует наибольшего количества кода.
3. @Webb Основываясь на вашем другом вопросе, который был недавно удален, вы, вероятно, захотите придерживаться подхода, основанного на цикле, или использовать циклическую версию с ОГРАНИЧЕНИЕМ НА МАССОВЫЙ СБОР ДЛЯ ВСЕХ версий
dbms_lock.sleep(300);
илиdbms_session.sleep(300);
(в 19c) после каждой итерации. Когда вы пытаетесь намеренно замедлить репликацию, к сожалению, вам часто приходится запускать неэффективный и более сложный код. На самом деле, вам может потребоваться еще один цикл для перезапуска процесса, потому что, если вы будете держать ВЫБОР открытым слишком долго, вы столкнетесь с ошибкой ОТМЕНЫ.4. @Webb И, вероятно, причина, по которой к вашему другому вопросу отнеслись плохо, заключается в том, что людям трудно понять, насколько репликация может разрушить разумное решение, если они не видели, как это происходит. Когда вы задаете подобные вопросы или пишете такой код, полезно заранее добавить отказ от ответственности, объясняющий, почему простое решение не сработает.