如何在BEGIN之后声明游标?

时间:2015-10-28 09:15:42

标签: oracle syntax plsql cursor

我想知道是否可以在BEGIN之后声明游标。

如何将plsql的结果导出到Excel工作表,因为我必须将此过程作为工作运行。

CREATE OR REPLACE PROCEDURE masc(v_amsprogramid VARCHAR2) AS

  v_mid VARCHAR2(50);
  v_sid VARCHAR2(50);

  CURSOR c1 IS
    SELECT DISTINCT mid
    FROM table_a WHERE aid = v_aid
    ORDER BY mid;

  BEGIN

    OPEN c1;

    LOOP
      FETCH c1 INTO v_mid;
      EXIT WHEN c1%NOTFOUND;

      DECLARE
        CURSOR c2 IS
          SELECT DISTINCT sid INTO v_sid
          FROM table_b WHERE mid = v_mid;

      BEGIN
        OPEN c2;
        LOOP

          FETCH c1 INTO v_mid;
          EXIT WHEN c1%NOTFOUND;

          dbms_output.PUT_LINE('MID : ' || v_mid);
          dbms_output.PUT_LINE('Sid : ' || v_sid);

        END LOOP;

        CLOSE c2;
      END LOOP;

      CLOSE c1;
  END masc;

4 个答案:

答案 0 :(得分:3)

也许你正在寻找这个:

create or replace PROCEDURE MASC (V_AMSPROGRAMID VARCHAR2) AS

V_MID VARCHAR2(50);
V_SID VARCHAR2(50);

CURSOR C1 IS
SELECT distinct MID from table_a WHERE AID = V_AID
ORDER BY MID;

CURSOR C2 IS
SELECT DISTINCT SID INTO V_SID FROM table_b WHERE MID = V_MID
ORDER BY MID;

BEGIN    
...

create or replace PROCEDURE MASC (V_AMSPROGRAMID VARCHAR2) AS

V_MID VARCHAR2(50);
V_SID VARCHAR2(50);

CURSOR C1 IS
SELECT distinct MID from table_a WHERE AID = V_AID
ORDER BY MID;

CURSOR C2(v in NUMBER) IS
SELECT DISTINCT SID INTO V_SID FROM table_b WHERE MID = v
ORDER BY MID;

BEGIN

OPEN C1;
...
OPEN C2(V_MID);
...

答案 1 :(得分:2)

  

我想知道我是否可以在开始后声明游标

不完全是。但您可以使用光标进行循环,而不是声明显式光标

例如,

FOR i IN (SELECT distinct MID from table_a WHERE AID = V_AID ORDER BY MID)
LOOP
   <do something>
END LOOP;

但无论如何,这会慢一些,因为逐行缓慢。我根本不认为需要程序。如果您确实需要在 PL / SQL 中进行,请考虑 BULK COLLECT

  

如何将plsql的结果导出到excel表,因为我将此过程作为作业运行。

在这种情况下,我认为不需要 PL / SQL 。您只需在 SQL * Plus 中使用 SPOOL

例如,

sqlplus user/pass@service_name
<required formatting options>

SPOOL /location/myfile.csv
SELECT distinct MID from table_a WHERE AID = V_AID ORDER BY MID;
SPOOL OFF

答案 2 :(得分:0)

U可以使用参考光标

            create or replace PROCEDURE MASC (V_AMSPROGRAMID VARCHAR2) AS

            V_MID VARCHAR2(50);
            V_SID VARCHAR2(50);

            C1 sys_refcursor ;
            c2 sys_refcursor ;

            BEGIN

            OPEN C1 for SELECT distinct MID from table_a WHERE AID = V_AID
            ORDER BY MID;

            LOOP

            FETCH C1 INTO V_MID;
            EXIT WHEN C1%NOTFOUND;


            open C2 for SELECT DISTINCT SID INTO V_SID FROM table_b WHERE MID = V_MID;

            LOOP

            FETCH C1 INTO V_MID;
            EXIT WHEN C1%NOTFOUND;

            DBMS_OUTPUT.PUT_LINE('MID : ' || V_MID);
            DBMS_OUTPUT.PUT_LINE('Sid : ' || V_SID);



            END LOOP;

            CLOSE C2;
            CLOSE C1;
            END LOOP;

答案 3 :(得分:0)

您可以在同一个pl / sql块中声明多个游标。在打开第一个光标后,无需声明第二个光标!

你会写一些类似的东西:

create or replace procedure masc (p_amsprogramid varchar2)
as
  v_mid varchar2(50);
  v_sid varchar2(50);

  cursor c1 
  is
    select   distinct mid
    from     table_a
    where    aid = p_amsprogramid
    order by mid;

  cursor c2
  is
    select distinct sid
    from   table_b
    where  mid = v_mid;

begin
  open c1;
  loop
    fetch c1 into v_mid;
    exit when c1%notfound;

    open c2;
    loop
      fetch c1 into v_mid;
      exit when c1%notfound;

      dbms_output.put_line('mid : ' || v_mid);
      dbms_output.put_line('sid : ' || v_sid);
    end loop;

    close c2;
  end loop;

  close c1;
end masc;
/

但是,如果你将open-cursor-loop-fetches替换为for-for-loop,你可以稍微简化一下:

create or replace procedure masc (p_amsprogramid varchar2)
as
  cursor c1 
  is
    select   distinct mid
    from     table_a
    where    aid = p_amsprogramid
    order by mid;

  cursor c2
  is
    select distinct sid
    from   table_b
    where  mid = v_mid;

begin
  for rec1 in c1
  loop
    for rec2 in c2
    loop
      dbms_output.put_line('mid : ' || rec1.mid);
      dbms_output.put_line('sid : ' || rec2.sid);
    end loop;
  end loop;
end masc;
/

看着它,你有一个嵌套的游标循环。这会尖叫程序性思维,而不是基于集合的思维,当你在数据库中处理数据集时,这是一个非常大的禁忌(即,它很慢。你需要不断在SQL和PL / SQL引擎之间切换,而不是简单地要求SQL引擎在将其提供给PL / SQL引擎之前计算所有内容。)

通过嵌套游标循环,你基本上重新发明了NESTED LOOP连接 - 这个SQL引擎可以做得比你做得好得多(更不用说它可能不是最有效的连接,而SQL引擎可以选择一种更好的加入方式!)。每次看到嵌套的游标循环时,都应该立即停止并查看是否可以将查询合并到一个select语句中。 (实际上,每当你看到一个循环时,你应该暂停并考虑你是否确实需要它;有时它是必要的,但是如果你做了一些事情,比如选择一组结果然后经过每一行然后进行更新,考虑将select合并到更新中,以便您有一个语句一次更新所有行。它会更快!)

例如,您的原始程序可以重写为:

create or replace procedure masc (p_amsprogramid varchar2)
as
  cursor c1 
  is
    select   distinct a.mid,
                      b.sid
    from     table_a a
             inner join table_b b on (a.mid = b.mid)
    where    a.aid = p_amsprogramid
    order by mid;

begin
  for rec1 in c1
  loop
    dbms_output.put_line('mid : ' || rec1.mid);
    dbms_output.put_line('sid : ' || rec1.sid);
  end loop;
end masc;
/

阅读,理解和维护更简单,我认为你会同意!

如果您想要将sql查询的结果作为文件写出来,则需要使用UTL_FILE,而不是DBMS_OUTPUT。请记住,写入文件的目录需要是挂载/映射到数据库所在服务器的目录。如果将结果写为字符分隔符,则可以轻松地将该文件导入Excel。

您可能会找到this to be of use