使用显式游标和循环,找不到错误

Using explicit cursor and loop, can't find error

我正在尝试找出用于向名为 MORE_THAN_ONE 的 ATA_ENTERTAINER table 添加新列的代码,数据类型为 NUMBER(此代码将在 PL/SQL代码)。如果艺人拥有多种风格类型,则此列将包含艺人拥有的风格类型的数量(如果他们有一种,则在此列中放置 NULL)。

我的代码应该使用明确的游标和循环结构遍历每个艺人并确定他们拥有的样式数。如果一个艺人有多个,我需要用样式数修改该艺人的 MORE_THAN_ONE 列中的值(如果不超过一个,则应将 NULL 放入此列)。我还需要使用 FOR UPDATE 和 WHERE CURRENT OF 作为我的解决方案的一部分。使用基本循环来解决这个问题, 以及任何决策结构的 IF。

这就是我现在拥有的。 它没有显示任何错误,但不知何故,当我 运行 这段代码时,我的 table 没有 update/change。

DECLARE
    ata_entId ata_entertainer.entertainer_id%TYPE; 
    ata_st_entId ata_entertainers_style.entertainer_id%TYPE; 
    ata_more ata_entertainer.more_than_one%TYPE;
    ata_count NUMBER(10) := 0; 

    CURSOR ata_rec IS 
        SELECT  entertainer_id
        FROM ata_entertainers_style
        WHERE ata_st_entId = ata_entId
        FOR UPDATE;
BEGIN 
    OPEN ata_rec; 
    LOOP 
    FETCH  ata_rec INTO ata_st_entId; 
    EXIT WHEN ata_rec%NOTFOUND; 
    IF ata_st_entId = ata_entId THEN ata_more := ata_count+1; 
    UPDATE ata_entertainer SET more_than_one = ata_more
    WHERE CURRENT of ata_rec;
    END IF;
    END LOOP;
    CLOSE ata_rec; 
    END;
    /

想想你自己会怎么做。你有一个风格代码和艺人 ID 的列表。您需要遍历列表并计算同一个艺人 ID 出现的次数。您必须为列表中的每个不同的艺人 ID 执行此操作。这意味着要多次浏览列表。如果您可以按艺人 ID 对列表进行排序怎么办?然后您只需要遍历列表一次,因为具有相同艺人 ID 的所有行都出现在一起。因此你的光标应该是...

cursor ATA_REC is
  select ENTERTAINER_ID
    from ATA_ENTERTAINERS_STYLE
   order by ENTERTAINER_ID
for update;

所以你开始浏览列表中的行。只要当前行的艺人 ID 与上一行的相同,就可以增加计数。新的艺人ID一出现,就开始新的计数对吧?此外,当艺人 ID 更改时,您拥有最后一个艺人 ID 的样式数。如果该计数超过一 (1),则需要更新 ATA_ENTERTAINER table.

这是我的解决方案。请注意,它编译(在 Oracle 11g Express Edition 中)但我没有在样本数据上测试它(太懒了:-)

declare
  ATA_ENT_ID        ATA_ENTERTAINER.ENTERTAINER_ID%type;
  L_ENTERTAINER_ID  ATA_ENTERTAINER.ENTERTAINER_ID%type;
  L_FIRST           boolean;
  L_SUM             number(3); -- Assume less than one thousand styles for single entertainer.
--
  cursor ATA_REC is
    select ENTERTAINER_ID
      from ATA_ENTERTAINERS_STYLE
     order by ENTERTAINER_ID
    for update;
begin
  L_ENTERTAINER_ID := -1;
  L_FIRST := true;
  L_SUM := 0;
  open ATA_REC;
  loop
    fetch ATA_REC into ATA_ENT_ID;
    exit when ATA_REC%notfound;
    if ATA_ENT_ID = L_ENTERTAINER_ID then
      L_SUM := L_SUM + 1;
    else
      if L_FIRST then
        L_FIRST := false;
        L_SUM := 1;
        L_ENTERTAINER_ID := ATA_ENT_ID;
      else
        if L_SUM > 1 then
          update ATA_ENTERTAINER
             set MORE_THAN_ONE = L_SUM
           where current of ATA_REC;
        end if;
      end if;
      L_SUM := 0;
      L_ENTERTAINER_ID := ATA_ENT_ID;
    end if;
  end loop;
--
  -- Make sure we update the last entertainer.
  if L_SUM > 1 then
    update ATA_ENTERTAINER
       set MORE_THAN_ONE = L_SUM
     where current of ATA_REC;
  end if;
end;

解决方案可能是这个:

DECLARE
    ata_more ata_entertainer.more_than_one%TYPE;

    CURSOR ata_rec IS 
        SELECT entertainer_id
        FROM ata_entertainer
        FOR UPDATE;
BEGIN 

   for aEntertainer in ata_rec LOOP
      select count(*)
      into ata_more
      from ATA_ENTERTAINERS_STYLE
      WHERE entertainer_id = aEntertainer.entertainer_id;
      IF ata_more > 1 then
          UPDATE ata_entertainer SET more_than_one = ata_more
          WHERE CURRENT of ata_rec;
      END IF;
   END LOOP;
END;

实际上我并不清楚 WHERE CURRENT OF 是否适用于 FOR ... IN LOOP - 我留给你去发现。

在现实生活中,您会 运行 使用一条语句进行此更新:

update ata_entertainer a SET more_than_one = 
   (select NULLIF(count(*), 1) 
    FROM ATA_ENTERTAINERS_STYLE b 
    where a.entertainer_id = b.entertainer_id);