PL/SQL : 如何在程序中显示主题名称和讲师姓名?

PL/SQL : How to display subject title and lecturer names in a procedure?

我的程序将接收一个主题代码作为参数,然后继续显示主题名称和 2016 年之前教授过该主题的讲师姓名。我的程序还应该提示输入一个主题代码并显示结果。我按以下方式尝试了我的代码,但没有将它放入过程中,它 returns 得到了正确的结果,但是当我将我的代码作为 PL/SQL 过程的一部分包含时,我收到一条错误消息

Warning: Procedure created with compilation errors.

我对 PL/SQL 很陌生,所以任何建议都会有所帮助!我试了几个小时的程序代码如下:

    CREATE OR REPLACE PROCEDURE WHO(CODE CHAR)
IS
FNAME VARCHAR(256)
LNAME VARCHAR(256)
TITLE VARCHAR(256)

CURSOR C1
IS
    SELECT FIRST_NAME, LAST_NAME, SUBJECT.NAME
    FROM ACADEMIC INNER JOIN TEACHES
    ON STAFF# = LECTURER
    INNER JOIN SUBJECT
    ON TEACHES.CODE=SUBJECT.CODE
    WHERE YEAR<2016 AND TEACHES.CODE=CODE
    GROUP BY FIRST_NAME, LAST_NAME, SUBJECT.NAME;
BEGIN
    OPEN C1;
    LOOP
    IF C1%NOTFOUND THEN EXIT;
    END IF;
    FNAME=FIRST_NAME;
    LNAME=LAST_NAME;
    DBMS.OUTPUT.PUT_LINE(FNAME||' '||LNAME);
    TITLE=SUBJECT.NAME;
    DBMS.OUTPUT.PUT_LINE(TITLE);
    END LOOP;
    CLOSE C1;
END WHO;
/

您的代码几乎没有问题。贴标时缺少一些半成品。见下文:

CREATE OR REPLACE PROCEDURE WHO( CODE CHAR)
IS
  FNAME VARCHAR(256); --<Missing Semicolon
  LNAME VARCHAR(256); --<Missing Semicolon
  TITLE VARCHAR(256); --<Missing Semicolon

  CURSOR C1
  IS
    SELECT FIRST_NAME,
      LAST_NAME,
      SUBJECT.NAME
    FROM ACADEMIC
    INNER JOIN TEACHES
    ON STAFF# = LECTURER
    INNER JOIN SUBJECT
    ON TEACHES.CODE =SUBJECT.CODE
    WHERE YEAR      <2016
    AND TEACHES.CODE=CODE
    GROUP BY FIRST_NAME,
      LAST_NAME,
      SUBJECT.NAME;
BEGIN
  OPEN C1;
  LOOP    
    --this is how you put the value of cursor to variable
    FETCH C1 into FNAME ,LNAME ,TITLE ;

    --No need for IF to exit loop. You can use as shown below
    EXIT when C1%NOTFOUND;    

    DBMS_OUTPUT.PUT_LINE(FNAME||' '||LNAME);
    DBMS_OUTPUT.PUT_LINT(TITLE);
  END LOOP;
  CLOSE C1;
END WHO;
/

演示:

CREATE OR REPLACE PROCEDURE WHO( CODE CHAR)
IS  
  FNAME number;  
  CURSOR C1
  IS
    SELECT contract_id FROM TX;

BEGIN
  OPEN C1;
  LOOP    
    fetch c1 into FNAME;
    EXIT when C1%NOTFOUND;    

    DBMS_OUTPUT.PUT_LINE(FNAME);

  END LOOP;
  CLOSE C1;
END WHO;
/

输出:

1
1
1
2
2
2
3
3

你的方向是正确的,但是有一堆错别字..

但是对于你的主要问题:

您必须 FETCH 将这些行放入您的变量中。举个例子。

DECLARE
    FNAME   VARCHAR (256); -- you need some semicolons here ;)
    LNAME   VARCHAR (256);
    NAME    VARCHAR (256);
    TITLE   VARCHAR (256);

    CURSOR C1 -- replaced you query with a dummy-query. Yours should also work
    IS
        SELECT 'Hans' FIRST_NAME, 'Schnitzel' LAST_NAME, 'The Schnitzel' NAME
          FROM DUAL
        UNION ALL
        SELECT 'Heinrich' FIRST_NAME, 'Wurst' LAST_NAME, 'The Sausage' NAME
          FROM DUAL;
BEGIN
    OPEN C1; -- open cursor

    LOOP
        FETCH C1 INTO FNAME, LNAME, NAME; -- try to read the row and put the values into our variables

        IF C1%NOTFOUND -- check if we got a row
        THEN
            EXIT;
        END IF;

        DBMS_OUTPUT.put_line ('Name: ' || FNAME || ' ' || LNAME); -- work with is.
        TITLE := NAME;
        DBMS_OUTPUT.put_line ('Title: ' || TITLE);
    END LOOP;

    CLOSE C1;
END;

SQL Developer、PL/SQL Developer 等工具会高亮显示编译错误,或者在 SQL*Plus 命令行输入

show errors

编译后立即,否则

show errors procedure who

或者您可以查询user_errorstable。无论您使用什么工具,都需要能够处理编译错误。

首先整理代码,使其更易于阅读和遵循,并且总体上看起来更专业。此外,尽管它们实际上并没有导致任何错误,但您确实应该将这些 charvarchar 类型更改为标准 varchar2 (理想情况下,所有内容都应锚定为 academic.first_name%type 等,但是一次做一件事)。

这里是固定版本:

create or replace procedure who
    ( code subject.code%type )  -- CHAR is a disastrous datatype you should never need
is
    fname varchar2(50);  -- VARCHAR2 is the standard string type, VARCHAR is reserved
    lname varchar2(50);  -- Also PL/SQL requires terminating semicolons here
    title varchar2(256);

    cursor c1 is
        select first_name
             , last_name
             , subject.name
        from   academic
               join teaches
                    on  staff# = lecturer
               join subject
                    on  teaches.code = subject.code
        where  year < 2016
        and    teaches.code = who.code  -- "code" on its own is ambiguous
        group by first_name, last_name, subject.name;

begin
    open c1;
    loop
        fetch c1 into fname, lname, title;  -- Added 'fetch into'

        if c1%notfound then
            exit;
        end if;

        dbms_output.put_line(fname || ' ' || lname);  -- It's dbms_output, with a '_'
        dbms_output.put_line(title);
    end loop;

    close c1;
end who;

编译通过。 但是你仍然可以通过使用 Cursor FOR Loop 而不是复杂、冗长的方式来简化它:

create or replace procedure who
    ( code subject.code%type )
is
begin
    for r in (
        select first_name
             , last_name
             , subject.name as title
        from   academic
               join teaches
                    on  staff# = lecturer
               join subject
                    on  teaches.code = subject.code
        where  year < 2016
        and    teaches.code = who.code
        group by first_name, last_name, subject.name
    )
    loop
        dbms_output.put_line(r.first_name || ' ' || r.last_name);
        dbms_output.put_line(r.title);
    end loop;
end who;

这隐式声明了一个游标和一个名为 r 的记录,其中包含基于游标的三个字段,它会为您处理打开、获取和关闭。

我不知道你的 table,但我猜他们是这样的:

create table subject
( code        varchar2(20) primary key
, name        varchar2(30) not null );

create table academic
( staff#      integer primary key
, first_name  varchar2(20) not null
, last_name   varchar2(20) not null );

create table teaches
( lecturer    references academic(staff#) not null
, code        references subject not null
, year        number(4,0) not null
, constraint teaches_pk primary key (lecturer, code, year) );

如果是这样,我会在查询中使用 table 别名以避免歧义:

select a.first_name
     , a.last_name
     , s.name as title
from   academic a
       join teaches t
            on  t.lecturer = a.staff#
       join subject s
            on  s.code = t.code
where  t.year < 2016
and    t.code = who.code
group by a.first_name, a.last_name, s.name