Обновление в PL / SQL Блок дает ошибку PLS-00103 - PullRequest
2 голосов
/ 18 февраля 2020

Я пытаюсь создать блок PL/SQL для обновления пустого столбца is_true в новой таблице created_table на основе другой таблицы table1. Поля id в таблицах совпадают, и в created_table нет строк, в которых нет соответствующей строки в table1. Я основываю обновление на столбце состояния table1, но вместо «Да» и «Нет» мне нужно, чтобы оно отображалось как «Y» и «N»:

created_table:                         expected results:
id | is_true | other columns           id | is_true | other columns
---|---------|--------------           ---|---------|--------------
 1 | null    | ...                      1 | 'Y'     | ...          
 2 | null    | ...                      2 | 'N'     | ...          

table1:
id | status | other columns
---|--------|--------------
 1 | 'Yes'  | ...          
 2 | 'No'   | ...     

Поскольку created_table очень большой, я пытаюсь обновить его, используя процедуру PL/SQL, так что в случае сбоя на полпути, я все равно буду обновлять строки. При следующем запуске процедуры можно будет определить, где она ранее не работала, без обработки уже обработанных строк.

Я пробовал тестировать с этим блоком кода:

DECLARE
  is_true varchar2 (5) created_table.is_true%type; 
BEGIN
  FOR status IN (SELECT a.status 
                 from table1 a 
                 left join created_table b
                 where and a.id=b.id ) 
  LOOP
        IF  status = 'Yes' THEN
            UPDATE created_table SET is_true= 'Y'
        ELSE
            UPDATE created_table SET is_true= 'N'
                WHERE ROWNUM := status.ROWNUM
        END IF;
        DBMS_OUTPUT.PUT_LINE('Done');
    END LOOP;
END;

Но это дает мне ошибки :

PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:

   * & = - + ; < / > at in is mod remainder not rem

Что я могу сделать, чтобы это работало?

1 Ответ

1 голос
/ 21 февраля 2020

В вашем коде было несколько ошибок:

DECLARE
--  is_true varchar2 (5) created_table.is_true%type; -- PLS-00103: Encountered the symbol "CREATED_TABLE" when expecting one of the following:
  is_true created_table.is_true%type;
BEGIN
  FOR status IN (SELECT a.status 
                 from table1 a 
--                 left join created_table b  -- ORA-00905: missing keyword
--                 where and a.id=b.id ) 
                 left join created_table b on a.id = b.id)
  LOOP
--        IF  status = 'Yes' THEN -- PLS-00306: wrong number or types of arguments in call to '='
        IF  status.status = 'Yes' THEN
--            UPDATE created_table SET is_true= 'Y' -- ORA-00933: SQL command not properly ended
            UPDATE created_table SET is_true= 'Y';
        ELSE
            UPDATE created_table SET is_true= 'N'
--                WHERE ROWNUM := status.ROWNUM  -- ORA-00920: invalid relational operator and ORA-00933: SQL command not properly ended
                WHERE ROWNUM = status.ROWNUM;
        END IF;
        DBMS_OUTPUT.PUT_LINE('Done');
    END LOOP;
END;  -- PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
/

PLS-00103 на самом деле просто говорит вам, что / отсутствует. После этого оставшаяся ошибка - PLS-00302: component 'ROWNUM' must be declared. Из "Oracle База данных онлайновой документации": rownum - псевдостолбец .

Ваш SQL будет обновлять каждую строку в настройке таблицы is_true до 'Y' при первом столкновении status, являющемся 'Yes', поскольку вы не дали это WHERE пункт. Я предполагаю, что это не ваше намерение.

В вашем блоке PL/SQL также было нет COMMIT, так что в действительности это было бы так же, как и обычный SQL.


Исходя из описанной вами ситуации, я внес некоторые изменения в блок кода. Это обеспечит ограничение на количество строк, которые могут быть обработаны до COMMIT. Я установил лимит на 5. Вы должны изменить это на что-то подходящее. Он не будет подбирать строки с пустыми значениями для is_true, поэтому он будет работать только с необработанными строками:

DECLARE
  commit_counter PLS_INTEGER := 1; -- count commits
  commit_limit   PLS_INTEGER := 5; -- rows for commit limit
  counter        PLS_INTEGER := commit_limit;
BEGIN
  FOR rec IN (SELECT a.status, a.id 
                FROM created_table b
                JOIN table1 a ON a.id = b.id
               WHERE b.is_true IS NULL) -- do not pick up processed rows
  LOOP
        IF  rec.status = 'Yes' THEN
            UPDATE created_table SET is_true = 'Y'
             WHERE id = rec.id;
        ELSE
            UPDATE created_table SET is_true = 'N'
             WHERE id = rec.id;
        END IF;

        counter := counter - 1;

        IF counter < 1 THEN
          counter := commit_limit; --reset counter
          commit_counter := commit_counter + 1;
          COMMIT;
        END IF;
  END LOOP;

  COMMIT; -- all rows are processed;

  DBMS_OUTPUT.PUT_LINE(commit_counter || ' COMMITS');
END;
/

Это обновит все строки в одной go, все еще обновляются только «пустые» строки:

UPDATE created_table b
   SET is_true = (SELECT CASE a.status WHEN 'Yes' THEN 'Y'
                                       ELSE 'N'
                         END 
                    FROM table1 a
                   WHERE a.id = b.id)
 WHERE b.is_true IS NULL;
...