使用选择而不是表的游标循环(Oracle)

Posted

技术标签:

【中文标题】使用选择而不是表的游标循环(Oracle)【英文标题】:Cursor for loop using a selection instead of a table ( Oracle ) 【发布时间】:2015-03-16 22:17:35 【问题描述】:

我正在编写一个从父表填充子表的过程。然而,子表比父表具有更多的字段(应该如此)。我变出一个指向选择的游标,它本质上是多个表的连接。 这是我到目前为止得到的代码:

CREATE OR REPLACE PROCEDURE Pop_occ_lezione
AS
x Lezione%rowtype;
CURSOR cc IS 
WITH y as(
            SELECT  Codice_corso, 
                    nome_modulo, 
                    Data_inizio_ed_modulo diem, 
                    Giorno_lezione, 
                    ora_inizio_lezione o_i, 
                    ora_fine_lezione o_f, 
                    anno, 
                    id_cdl, 
                    nome_sede, 
                    locazione_modulo loc
            FROM    lezione 
                    join ( select id_cdl, anno, codice_corso from corso ) using (codice_corso)
                    join ( select codice_corso, locazione_modulo from modulo ) using (codice_corso)
                    join ( select nome_sede, id_cdl from cdl  ) using (id_cdl)
            WHERE
                case
                    when extract (month from Data_inizio_ed_modulo) < 9 then extract (year from Data_inizio_ed_modulo) - 1
                    else extract (year from Data_inizio_ed_modulo)
                    end = extract (year from sysdate+365)
        )
SELECT *
FROM y
WHERE sem_check(y.diem,sysdate+365) = 1;
--
BEGIN
FETCH cc into x;
EXIT when cc%NOTFOUND;
INSERT INTO Occr_lezione 
VALUES (
            x.Codice_corso,
            x.Nome_modulo,
            x.diem,x.giorno_lezione,
            x.Ora_inizio_lezione,
            to_date(to_char(next_day(sysdate,x.Giorno_lezione),'DD-MM-YYYY') || to_char(x.Ora_inizio_lezione,' hh24:mi'),'dd-mm-yyyy hh24:mi'),
            to_date(to_char(next_day(sysdate,x.Giorno_lezione),'DD-MM-YYYY') || to_char(x.Ora_fine_lezione,' hh24:mi'),'dd-mm-yyyy hh24:mi'),
            x.nome_sede,
            0,
            x.loc
        );
END LOOP;
END;
/

但它当然不会起作用,因为变量 x 具有我的初始表行的类型,它的列数少于我的选择。不幸的是,据我所知,需要一个行类型变量来循环游标,以便从中获取数据。你能看出矛盾吗?如何更改代码?是否可以制作某种类型的变量来反映我的查询结果中的一行?或者可能是一种在不使用支持变量的情况下循环浏览游标中数据的方法?或者可能是完全不同的东西?请告诉我。

好的,所以按照建议我尝试了这样的事情:

INSERT INTO Occr_lezione(
                         Codice_corso,
                         Nome_modulo,
                         Data_inizio_ed_modulo,
                         Giorno_lezione,
                         Ora_inizio_lezione,
                         Ora_fine_lezione,
                         Anno,
                         Id_cdl,
                         Nome_sede,
                         Locazione_modulo
                        )
WITH y as(
            SELECT  Codice_corso, 
                    Nome_modulo, 
                    Data_inizio_ed_modulo, 
                    Giorno_lezione, 
                    Ora_inizio_lezione, 
                    Ora_fine_lezione, 
                    Anno, 
                    Id_cdl, 
                    Nome_sede, 
                    Locazione_modulo
            FROM    Lezione 
                    join ( select Id_cdl, Anno, Codice_corso from Corso ) using (codice_corso)
                    join ( select Codice_corso, Locazione_modulo from Modulo ) using (Codice_corso)
                    join ( select Nome_sede, Id_cdl from Cdl  ) using (id_cdl)
            WHERE
                case
                    when extract (month from Data_inizio_ed_modulo) < 9 then extract (year from Data_inizio_ed_modulo) - 1
                    else extract (year from Data_inizio_ed_modulo)
                    end = extract (year from sysdate+365)
        )
SELECT *
FROM y
WHERE sem_check(y.Data_inizio_ed_modulo,sysdate+365) = 1;
END;
/

但它说 PL/SQL: ORA-00904: "LOCAZIONE_MODULO": invalid identifier

这不是真的,因为查询返回的表中存在这样的列...我错过了什么吗? 代码编译没有错误,当我尝试触发程序时发生。 如您所见,在 Occr_lezione 表中:

CREATE TABLE Occr_lezione (
Codice_corso                varchar2(20)    NOT NULL,
Nome_modulo                 varchar2(50)    NOT NULL,
Data_inizio_ed_modulo       date            NOT NULL,
Giorno_lezione              number(1)       NOT NULL,
Ora_inizio_lezione          date            NOT NULL,
Data_inizio_occr_lezione    date,
Data_fine_occr_lezione      date            NOT NULL,
Nome_sede                   varchar2(30)    NOT NULL,
Num_aula                    varchar2(3)     NOT NULL,
Tipo_aula                   varchar2(20)    NOT NULL,
--
CONSTRAINT fk_Occr_lezione_lezione  FOREIGN KEY (Codice_corso,Nome_modulo,Data_inizio_ed_modulo,Giorno_lezione,Ora_inizio_lezione) REFERENCES Lezione(Codice_corso,Nome_modulo,Data_inizio_ed_modulo,Giorno_lezione,Ora_inizio_lezione) ON DELETE CASCADE,
CONSTRAINT fk_Occr_lezione_aula     FOREIGN KEY (Nome_sede,Num_aula,Tipo_aula)  REFERENCES Aula(Nome_sede,Num_aula,Tipo_aula) ON DELETE SET NULL,
CONSTRAINT pk_Occr_lezione          PRIMARY KEY (Codice_corso,Nome_modulo,Data_inizio_ed_modulo,Giorno_lezione,Ora_inizio_lezione,Data_inizio_occr_lezione),
CHECK       ( trunc(Data_inizio_occr_lezione) = trunc(Data_fine_occr_lezione) ), -- data inizio = data fine // prenotazione giornaliera
CHECK       ( Data_inizio_occr_lezione < Data_fine_occr_lezione ) -- ora inizio < ora fine // coerenza temporale

没有名为 Locazione_modulo 的列,但是最后一列 Tipo_aula 与 Locazione modulo 的类型和大小相同:

CREATE TABLE Modulo (
Codice_corso        varchar2(20)    NOT NULL,
Nome_modulo         varchar2(50),
Locazione_modulo    varchar2(20)    NOT NULL,
--
CONSTRAINT fk_Modulo_Corso  FOREIGN KEY(Codice_corso) REFERENCES Corso(Codice_corso) ON DELETE CASCADE,
CONSTRAINT pk_Modulo        PRIMARY KEY(Codice_corso,Nome_modulo),
CHECK       (Locazione_modulo IN ('Aula','Laboratorio','Conferenze'))
);

所以应该是无关紧要的吧?

【问题讨论】:

错误发生在哪一行?编译器是否抱怨Occr_lezione 中没有LOCAZIONE_MODULO 列?还是它在抱怨您的SELECT 声明中对LOCAZIONE_MODULO 的两个引用之一?由于您没有提供任何表定义(并且由于您没有在查询中使用任何表别名来识别哪些列来自哪些表),因此很难猜测错误来自哪里。 编译器可以工作,但是当我尝试触发程序时,我得到了这个错误。 ORA-00904 是编译错误而不是运行时错误。除非您的实际过程正在使用您未在此处显示的动态 SQL 执行某些操作,否则我无法想象您如何没有收到编译错误,但是您在运行时遇到了编译错误。我强烈打赌您的CREATE PROCEDURE 调用会产生错误,但可能您使用的任何客户端应用程序都没有显示给您,或者没有让您注意到。 我在 sqlplus 终端上使用简单的 oracle 11g express edition。事实上,我是个白痴。这是一个编译错误。抱歉,我在这里 1:24 有点累了! 我不知道为什么它会变得艰难。 【参考方案1】:

如果您真的想使用显式游标,可以将x 声明为cc%rowtype 类型

CREATE OR REPLACE PROCEDURE Pop_occ_lezione
AS
  CURSOR cc IS ...
  x cc%rowtype;
...

除非您使用显式游标,因为您希望能够显式地将数据提取到本地集合中,以便稍后在您的过程中使用,否则使用隐式游标的代码往往是更可取的。这消除了FETCHCLOSE 游标或编写EXIT 条件的需要,并且它隐式执行批量获取以最小化上下文转换。

BEGIN
  FOR x IN cc
  LOOP
    INSERT INTO Occr_lezione ...
  END LOOP;
END;

当然,无论哪种情况,我都希望您为局部变量选择更有意义的名称——xcc 不要告诉您任何有关变量在做什么的信息。

如果您所做的只是从一组表中获取数据并将其插入到另一个表中,那么编写单个 INSERT 语句而不是编写 PL/SQL 循环会更有效。

INSERT INTO Occr_lezione( <<column list>> )
  SELECT <<column list>>
    FROM <<tables you are joining together in the cursor definition>>
   WHERE <<conditions from your cursor definition>>

【讨论】:

哇,谢谢,我刚刚发现我可以将光标用作行类型,但不知道可以直接从查询中插入,感谢您提供的信息! 所以,您可能会注意到我在选择中使用了别名,但我想我不能在插入中使用它们,因为它们尚未处理。是这样吗? @darkpirate - 我不确定我是否完全理解您的要求。如果您使用 CTE(或内联视图或类似的东西)构造您的 SELECT 查询,您仍然可以像您正在做的那样定义别名,您可以在外部查询中使用它们。当您执行单个 INSERT ... SELECT 时添加别名是否有用是您需要回答的问题。 你是对的。就我而言,没那么必要,顺便说一下,我已经更新了帖子,如果你能看一下就太好了:)

以上是关于使用选择而不是表的游标循环(Oracle)的主要内容,如果未能解决你的问题,请参考以下文章

Oracle动态游标实现动态SQL循环遍历,和静态游标的比较。

oracle的游标cursor

我的 Oracle SQL 代码正在编译我的代码块,该代码块将游标用于来自两个表的循环,但 dbms 输出行未显示结果

oracle 游标是做啥用的

sql 游标如何循环

关于游标和for循环的问题