PLSQL 过程需要很多时间来执行
PLSQL Procedure taking lot of time to execute
我有一个 Oracle PLSQL 过程,它有一个父游标和 2 个子游标(在主体中创建)参考父游标值。
当我直接运行 SQL 语句时,它会立即返回结果。但是,当我把它们放在 PLSQL 程序中时,它会 运行ning 不休。
CREATE OR REPLACE PROCEDURE SENDTASKREMINDER
AS
l_html VARCHAR2(32767);
r_html VARCHAR2(32767);
l_exst NUMBER;
ownerId NUMBER;
CURSOR OwnerCursor IS SELECT DISTINCT TASK_OWNER_ID FROM TASKS
WHERE to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY')
UNION
SELECT DISTINCT TASK_OWNER_ID FROM TASKS
WHERE to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY');
BEGIN
l_html := '<html>
<head>
<title>Task Reminder</title>
</head>
<body>';
r_html := l_html;
/* Get Pending Tasks */
OPEN OwnerCursor;
LOOP
FETCH OwnerCursor INTO ownerId;
r_html := l_html;
SELECT COUNT(*) INTO l_exst FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY');
IF l_exst > 0
THEN
r_html := r_html || '<h1>Your current pending tasks</h1><table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
FOR PendingCursor IN (SELECT PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY'))
LOOP
r_html := r_html || '<tr><td>' || PendingCursor.TASK_NAME || '</td><td>' || PendingCursor.TASK_DESCRIPTION || '</td><td>' || PendingCursor.TASK_DUE_DATE ||'</td></tr>';
END LOOP;
r_html := r_html || '</table>';
END IF;
SELECT COUNT(*) INTO l_exst FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY') ;
IF l_exst > 0
THEN
r_html := r_html || '<h1>Your future tasks</h1><table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
FOR FutureCursor IN (SELECT PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY') )
LOOP
r_html := r_html || '<tr><td>' || FutureCursor.TASK_NAME || '</td><td>' || FutureCursor.TASK_DESCRIPTION || '</td><td>' || FutureCursor.TASK_DUE_DATE ||'</td></tr>';
END LOOP;
r_html := r_html || '</table>';
END IF;
r_html := r_html || '</body></html>';
send_mail(p_to => 'XXX.XXX@xxx.com',
p_from => 'fromemail@xxx.com',
p_subject => 'Your Pending and Future tasks for OwnerId' || ownerId,
p_text_msg => 'text msg is not yet created',
p_html_msg => r_html,
p_smtp_host => 'localhost');
END LOOP;
CLOSE OwnerCursor;
END;
/
这里是否创建了无限循环。 PLSQL没有错误。在粘贴到这里之前我已经编辑了这个文件。所以,如果发现编译错误,那是因为我的编辑。实际程序没有任何编译错误。
在此先感谢您的帮助。
您在获取后忘记了 EXIT
:
FETCH Ownercursor into ownerId;
EXIT WHEN Ownercursor%NOTFOUND;
我没有看到任何无休止的循环。您是否在独立过程中测试了对 send_mail 的调用?它可能挂在那里。
您的 where 子句中还有另一个逻辑缺陷。您使用的 TO_CHAR 函数有误。这个:
AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY'))
在逻辑上不等同于此:
TASK_START_DATE >= SYSDATE AND TASK_DUE_DATE <= SYSDATE))
例如,如果 TASK_START_DATE 是 2015 年 4 月 1 日,TASK_DUE_DATE 是 2015 年 5 月 30 日,今天是 2015 年 6 月 1 日,那么您的表达式将计算为 TRUE,因为 "JUN" 按字母顺序在 "APR" 和 "MAY" 之间。
哇。从哪儿开始。 Diederikh 和 Scott 解决了几个明显的问题,但仔细观察代码,有很多地方没有意义。
首先,"Current" 任务的开始日期是过去还是现在,截止日期是未来还是现在? "Future" 任务的开始日期是否在未来?如果是这样,你的比较就全错了。根据您的逻辑,"Current" 任务的开始日期是未来或现在,截止日期是过去或现在。
其次,有五个(!)个地方有查询要出去并访问数据库。没有理由不能用初始光标读取您需要的所有内容。然后按需分析处理即可。
还有很多其他地方我会提出建议。我不想列出所有这些,所以我只包含代码。你可以接受我的小建议,也可以不接受,但一定要验证你的日期逻辑,不要比你绝对需要的更频繁地访问数据库。在执行一次磁盘访问所需的时间内可以执行数千行代码。它仍然没有我希望的生产代码那样完美,但足以让您入门。而且,虽然我无法访问您的 table,但它应该可以使用。
create or replace PROCEDURE SENDTASKREMINDER AS
l_html VARCHAR2(100);
r_html VARCHAR2(32767);
C_header varchar2( 40 ) := '<h1>Your current pending tasks</h1>';
F_header varchar2( 32 ) := '<h1>Your future tasks</h1>';
EndHeader varchar2( 80 ) := '<table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
EndFooter varchar2( 32 ) := '</table></body></html>';
PrevOwnerId int := -1; -- Must not be a valid OwnerID
C_count int := 0;
F_count int := 0;
l_today date := Trunc( SysDate );
CURSOR OwnerCursor IS
SELECT TASK_OWNER_ID, PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE
FROM TASKS
WHERE (TASK_START_DATE <= l_today and TASK_DUE_DATE >= l_today)
or TASK_START_DATE > l_today
order by TASK_OWNER_ID, TASK_START_DATE; -- So first the Current then Future tasks
BEGIN
l_html := '<html><head><title>Task Reminder</title></head><body>';
/* Get all Tasks */
for OwnerRec in OwnerCursor LOOP
if OwnerRec.TASK_OWNER_ID != PrevOwnerId then
-- This is the first record for this owner. Unless it's also the first record of the loop,
-- close the html and send the email.
if C_count > 0 or F_count > 0 then
r_html := r_html || EndFooter;
end if;
send_mail(p_to => 'XXX.XXX@xxx.com',
p_from => 'fromemail@xxx.com',
p_subject => 'Your Pending and Future tasks for OwnerId' || ownerId,
p_text_msg => 'text msg is not yet created',
p_html_msg => r_html,
p_smtp_host => 'localhost');
r_html := l_html;
PrevOwnerId := OwnerRec.TASK_OWNER_ID;
C_count := 0;
F_count := 0;
end if;
if OwnerRec.TASK_START_DATE <= l_today then
if C_count = 0 then
-- This is the first Current entry, write the header
r_html := r_html || C_header || EndHeader;
end if;
C_count := c_count + 1;
else
if F_count = 0 then
-- This is the first Future entry. See if there were any Current entries
if C_count > 0 then
-- There was, so terminate the Current table
r_html := r_html || '</table>';
C_count := 0;
end if;
-- Write the header
r_html := r_html || F_header || EndHeader;
end if;
F_count := F_count + 1;
end if;
-- Whether Current or Future, write the task particulars
r_html := r_html || '<tr><td>' || OwnerRec.TASK_NAME || '</td><td>' || OwnerRec.TASK_DESCRIPTION || '</td><td>' || OwnerRec.TASK_DUE_DATE ||'</td></tr>';
END LOOP;
-- The last owner has not completed processing. But maybe there were no tasks at all...
if C_count > 0 or F_count > 0 then
r_html := r_html || EndFooter;
send_mail(p_to => 'XXX.XXX@xxx.com',
p_from => 'fromemail@xxx.com',
p_subject => 'Your Pending and Future tasks for OwnerId' || ownerId,
p_text_msg => 'text msg is not yet created',
p_html_msg => r_html,
p_smtp_host => 'localhost');
dbms_output.put_line( r_html );
end if;
END;
我有一个 Oracle PLSQL 过程,它有一个父游标和 2 个子游标(在主体中创建)参考父游标值。
当我直接运行 SQL 语句时,它会立即返回结果。但是,当我把它们放在 PLSQL 程序中时,它会 运行ning 不休。
CREATE OR REPLACE PROCEDURE SENDTASKREMINDER
AS
l_html VARCHAR2(32767);
r_html VARCHAR2(32767);
l_exst NUMBER;
ownerId NUMBER;
CURSOR OwnerCursor IS SELECT DISTINCT TASK_OWNER_ID FROM TASKS
WHERE to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY')
UNION
SELECT DISTINCT TASK_OWNER_ID FROM TASKS
WHERE to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY');
BEGIN
l_html := '<html>
<head>
<title>Task Reminder</title>
</head>
<body>';
r_html := l_html;
/* Get Pending Tasks */
OPEN OwnerCursor;
LOOP
FETCH OwnerCursor INTO ownerId;
r_html := l_html;
SELECT COUNT(*) INTO l_exst FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY');
IF l_exst > 0
THEN
r_html := r_html || '<h1>Your current pending tasks</h1><table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
FOR PendingCursor IN (SELECT PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY'))
LOOP
r_html := r_html || '<tr><td>' || PendingCursor.TASK_NAME || '</td><td>' || PendingCursor.TASK_DESCRIPTION || '</td><td>' || PendingCursor.TASK_DUE_DATE ||'</td></tr>';
END LOOP;
r_html := r_html || '</table>';
END IF;
SELECT COUNT(*) INTO l_exst FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY') ;
IF l_exst > 0
THEN
r_html := r_html || '<h1>Your future tasks</h1><table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
FOR FutureCursor IN (SELECT PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE FROM TASKS
WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY') )
LOOP
r_html := r_html || '<tr><td>' || FutureCursor.TASK_NAME || '</td><td>' || FutureCursor.TASK_DESCRIPTION || '</td><td>' || FutureCursor.TASK_DUE_DATE ||'</td></tr>';
END LOOP;
r_html := r_html || '</table>';
END IF;
r_html := r_html || '</body></html>';
send_mail(p_to => 'XXX.XXX@xxx.com',
p_from => 'fromemail@xxx.com',
p_subject => 'Your Pending and Future tasks for OwnerId' || ownerId,
p_text_msg => 'text msg is not yet created',
p_html_msg => r_html,
p_smtp_host => 'localhost');
END LOOP;
CLOSE OwnerCursor;
END;
/
这里是否创建了无限循环。 PLSQL没有错误。在粘贴到这里之前我已经编辑了这个文件。所以,如果发现编译错误,那是因为我的编辑。实际程序没有任何编译错误。
在此先感谢您的帮助。
您在获取后忘记了 EXIT
:
FETCH Ownercursor into ownerId;
EXIT WHEN Ownercursor%NOTFOUND;
我没有看到任何无休止的循环。您是否在独立过程中测试了对 send_mail 的调用?它可能挂在那里。
您的 where 子句中还有另一个逻辑缺陷。您使用的 TO_CHAR 函数有误。这个:
AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY'))
在逻辑上不等同于此:
TASK_START_DATE >= SYSDATE AND TASK_DUE_DATE <= SYSDATE))
例如,如果 TASK_START_DATE 是 2015 年 4 月 1 日,TASK_DUE_DATE 是 2015 年 5 月 30 日,今天是 2015 年 6 月 1 日,那么您的表达式将计算为 TRUE,因为 "JUN" 按字母顺序在 "APR" 和 "MAY" 之间。
哇。从哪儿开始。 Diederikh 和 Scott 解决了几个明显的问题,但仔细观察代码,有很多地方没有意义。
首先,"Current" 任务的开始日期是过去还是现在,截止日期是未来还是现在? "Future" 任务的开始日期是否在未来?如果是这样,你的比较就全错了。根据您的逻辑,"Current" 任务的开始日期是未来或现在,截止日期是过去或现在。
其次,有五个(!)个地方有查询要出去并访问数据库。没有理由不能用初始光标读取您需要的所有内容。然后按需分析处理即可。
还有很多其他地方我会提出建议。我不想列出所有这些,所以我只包含代码。你可以接受我的小建议,也可以不接受,但一定要验证你的日期逻辑,不要比你绝对需要的更频繁地访问数据库。在执行一次磁盘访问所需的时间内可以执行数千行代码。它仍然没有我希望的生产代码那样完美,但足以让您入门。而且,虽然我无法访问您的 table,但它应该可以使用。
create or replace PROCEDURE SENDTASKREMINDER AS
l_html VARCHAR2(100);
r_html VARCHAR2(32767);
C_header varchar2( 40 ) := '<h1>Your current pending tasks</h1>';
F_header varchar2( 32 ) := '<h1>Your future tasks</h1>';
EndHeader varchar2( 80 ) := '<table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
EndFooter varchar2( 32 ) := '</table></body></html>';
PrevOwnerId int := -1; -- Must not be a valid OwnerID
C_count int := 0;
F_count int := 0;
l_today date := Trunc( SysDate );
CURSOR OwnerCursor IS
SELECT TASK_OWNER_ID, PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE
FROM TASKS
WHERE (TASK_START_DATE <= l_today and TASK_DUE_DATE >= l_today)
or TASK_START_DATE > l_today
order by TASK_OWNER_ID, TASK_START_DATE; -- So first the Current then Future tasks
BEGIN
l_html := '<html><head><title>Task Reminder</title></head><body>';
/* Get all Tasks */
for OwnerRec in OwnerCursor LOOP
if OwnerRec.TASK_OWNER_ID != PrevOwnerId then
-- This is the first record for this owner. Unless it's also the first record of the loop,
-- close the html and send the email.
if C_count > 0 or F_count > 0 then
r_html := r_html || EndFooter;
end if;
send_mail(p_to => 'XXX.XXX@xxx.com',
p_from => 'fromemail@xxx.com',
p_subject => 'Your Pending and Future tasks for OwnerId' || ownerId,
p_text_msg => 'text msg is not yet created',
p_html_msg => r_html,
p_smtp_host => 'localhost');
r_html := l_html;
PrevOwnerId := OwnerRec.TASK_OWNER_ID;
C_count := 0;
F_count := 0;
end if;
if OwnerRec.TASK_START_DATE <= l_today then
if C_count = 0 then
-- This is the first Current entry, write the header
r_html := r_html || C_header || EndHeader;
end if;
C_count := c_count + 1;
else
if F_count = 0 then
-- This is the first Future entry. See if there were any Current entries
if C_count > 0 then
-- There was, so terminate the Current table
r_html := r_html || '</table>';
C_count := 0;
end if;
-- Write the header
r_html := r_html || F_header || EndHeader;
end if;
F_count := F_count + 1;
end if;
-- Whether Current or Future, write the task particulars
r_html := r_html || '<tr><td>' || OwnerRec.TASK_NAME || '</td><td>' || OwnerRec.TASK_DESCRIPTION || '</td><td>' || OwnerRec.TASK_DUE_DATE ||'</td></tr>';
END LOOP;
-- The last owner has not completed processing. But maybe there were no tasks at all...
if C_count > 0 or F_count > 0 then
r_html := r_html || EndFooter;
send_mail(p_to => 'XXX.XXX@xxx.com',
p_from => 'fromemail@xxx.com',
p_subject => 'Your Pending and Future tasks for OwnerId' || ownerId,
p_text_msg => 'text msg is not yet created',
p_html_msg => r_html,
p_smtp_host => 'localhost');
dbms_output.put_line( r_html );
end if;
END;