SQL游标获取重复记录

时间:2014-01-03 15:16:55

标签: oracle loops plsql cursor

所以我的问题如下:我有3张桌子:
种类:
- Naam
- Omschrijving

Categorieabonnement:
- MailabonneeID
- CategorieNaam

Mailabonnee:
- ID
- Voornaam
- Achternaam
- Emailadres

首先,我根据分类中的Naam获取Mailabonnee中的ID。 (通过Categorieabonnement) 然后我想显示那些属于Naam的ID。这很好用。 现在我想得到Voornaam,Achternaam和Emailadres。这是下面的代码:

DECLARE 
mailabonneeID  number;
voornaam       varchar2(20);
achternaam     varchar2(20);
emailadres     varchar2(20);
CURSOR getabonnee IS
SELECT CATEGORIEABONNEMENT.MAILABONNEEID 
FROM CATEGORIE
INNER JOIN CATEGORIEABONNEMENT ON CATEGORIEABONNEMENT.CATEGORIENAAM = CATEGORIE.NAAM
WHERE NAAM = 'Sport';

CURSOR getabonneeinfo IS
SELECT MAILABONNEE.VOORNAAM, MAILABONNEE.ACHTERNAAM, MAILABONNEE.EMAILADRES
FROM CATEGORIEABONNEMENT
INNER JOIN MAILABONNEE ON MAILABONNEE.ID = CATEGORIEABONNEMENT.MAILABONNEEID
WHERE MAILABONNEEID = mailabonneeID;

BEGIN
OPEN getabonnee;

LOOP
  FETCH getabonnee
  INTO  mailabonneeID;
  EXIT WHEN getabonnee%NOTFOUND;
  DBMS_OUTPUT.put_line(mailabonneeID);


OPEN getabonneeinfo;

LOOP
  FETCH getabonneeinfo
  INTO voornaam, achternaam, emailadres;
  EXIT WHEN getabonneeinfo%NOTFOUND;
  DBMS_OUTPUT.put_line(voornaam);
  DBMS_OUTPUT.put_line(achternaam);
  DBMS_OUTPUT.put_line(emailadres);
END LOOP;

CLOSE getabonneeinfo;

END LOOP;

CLOSE getabonnee;
END;
/

目前,输出总是显示Mailabonnee的前4条记录,第三条记录显示两次。

任何想法如何解决这个问题?

目前的输出是:

1
一月 扬森 Jan@test.nl
皮特 Zanden Piet@test.nl
克拉斯 Vaak Klaas@test.nl
克拉斯 Vaak Klaas@test.nl
Fake1 de Faker a.nl
2
一月 扬森 Jan@test.nl
皮特 Zanden Piet@test.nl
克拉斯 Vaak Klaas@test.nl
克拉斯 Vaak Klaas@test.nl
Fake1 de Faker a.nl

虽然预期输出应为:
1
Jan Janssen Jan@test.nl
2
Piet Zanden Piet@test.nl

2 个答案:

答案 0 :(得分:2)

第二个游标中的WHERE子句是重言式(又名1 = 1)。

这是一个修复(使用隐式光标来节省空间:)

DECLARE
   CURSOR getabonnee IS
      SELECT categorieabonnement.mailabonneeid, naam
        FROM categorie
       INNER JOIN categorieabonnement
          ON categorieabonnement.categorienaam = categorie.naam
       WHERE naam = 'Sport';

   CURSOR getabonneeinfo(p_id Categorieabonnement.mailabonneeid%TYPE) IS
      SELECT mailabonnee.voornaam, mailabonnee.achternaam, 
             mailabonnee.emailadres
        FROM categorieabonnement
       INNER JOIN mailabonnee
          ON mailabonnee.id = categorieabonnement.mailabonneeid
       WHERE -- mailabonneeid = mailabonneeid <- true=true
             mailabonneeid = p_id;
BEGIN
   FOR cc IN getabonnee LOOP
      DBMS_OUTPUT.put_line(cc.mailabonneeID);

      FOR cc2 IN getabonneeinfo(cc.mailabonneeID) LOOP
         DBMS_OUTPUT.put_line(cc2.voornaam);
         DBMS_OUTPUT.put_line(cc2.achternaam);
         DBMS_OUTPUT.put_line(cc2.emailadres);
      END LOOP;
   END LOOP;
END;

当然在这种情况下,单个游标(有3个表)应该有效!

在任何情况下,如果您只为变量使用了前缀,那么您的WHERE子句就可以使用变量,这样就不会遇到variable shadowing problems

答案 1 :(得分:0)

退出第一个循环后,mailabonneeID的值为NULL,因此第二个循环不会返回任何内容。

这样做:

BEGIN
OPEN getabonnee;

LOOP
  FETCH getabonnee
  INTO  mailabonneeID;
  EXIT WHEN getabonnee%NOTFOUND;
  DBMS_OUTPUT.put_line(mailabonneeID);

   OPEN getabonneeinfo;
   LOOP
   FETCH getabonneeinfo
     INTO voornaam, achternaam, emailadres;
     EXIT WHEN getabonneeinfo%NOTFOUND;
     DBMS_OUTPUT.put_line(voornaam);
     DBMS_OUTPUT.put_line(achternaam);
     DBMS_OUTPUT.put_line(emailadres);
   END LOOP;
   CLOSE getabonneeinfo;

END LOOP;

CLOSE getabonnee;
END;

另一个通用提示:根据列定义声明变量,即在您的情况下:

mailabonneeID  CATEGORIEABONNEMENT.MAILABONNEEID%TYPE;
voornaam       MAILABONNEE.VOORNAAM%TYPE;
achternaam     MAILABONNEE.ACHTERNAAM%TYPE;
emailadres     MAILABONNEE.EMAILADRES%TYPE;

它使您的代码更加强大,可以防止错误和修改。