Ошибка ORACLE в цикле, необходимо заполнить одну переменную для каждого обновления

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