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_errors
table。无论您使用什么工具,都需要能够处理编译错误。
首先整理代码,使其更易于阅读和遵循,并且总体上看起来更专业。此外,尽管它们实际上并没有导致任何错误,但您确实应该将这些 char
和 varchar
类型更改为标准 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
我的程序将接收一个主题代码作为参数,然后继续显示主题名称和 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_errors
table。无论您使用什么工具,都需要能够处理编译错误。
首先整理代码,使其更易于阅读和遵循,并且总体上看起来更专业。此外,尽管它们实际上并没有导致任何错误,但您确实应该将这些 char
和 varchar
类型更改为标准 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