PLSQL Oracle游标程序
问题描述:
目前我正在学习PLSQL,使用Oracle。我正在尝试获取比另一个表中的参数日期更早的数据。我希望程序获取所有数据,检查一些记录是否比param_value中的参数早(recv_date),并且如果是,则要启动我的警报程序。我在声明一个CURSOR和ln_pallets_container时遇到了问题。我知道我可以以某种方式进入ln_pallets数据只有在recv_date我已经过滤,但在这里,我不知道如何正确地做到这一点。也许我应该在过程之前声明光标,而不是在它之内?PLSQL Oracle游标程序
procedure CHECK_STOCK_DATE(warehouse_id_in IN warehouse.warehouse_id%TYPE)
IS
ln_pallet_count NUMBER;
ln_days_till_expiration param_value.param_value%TYPE;
CURSOR ln_pallets IS
SELECT container_id, recv_date
FROM wms_stock ws
ln_pallets_container%ROWTYPE;
BEGIN
OPEN ln_pallets;
LOOP
FETCH ln_pallets INTO ln_pallets_container;
EXIT WHEN ln_pallets%NOTFOUND;
SELECT param_value.param_value
INTO ln_days_till_expiration
FROM param_value
WHERE param_value.parameter_id = 266;
IF(ln_pallets_container.recv_date >= trunc(sysdate - ln_days_till_expiration)
ALARM.ALARM(WAREHOUSE_ID =>MY_COMMONS.GET_WHRS_ID,
SOURCE_TEXT => ln_pallets_container.container_id,
MESSAGE_CODE => 'Cannot find this container on warehouse. Check container code.');
END IF;
END LOOP;
CLOSE ln_pallets;
END;
答
有几件事情你的代码错误,我已经固定,并在下面突出显示:
PROCEDURE check_stock_date(warehouse_id_in IN warehouse.warehouse_id%TYPE) IS
ln_pallet_count NUMBER;
ln_days_till_expiration param_value.param_value%TYPE;
CURSOR ln_pallets IS
SELECT container_id,
recv_date
FROM wms_stock ws; -- added semicolon
ln_pallets_container ln_pallets%ROWTYPE; -- amended to set the datatype of the variable to be the cursor rowtype
BEGIN
OPEN ln_pallets;
LOOP
FETCH ln_pallets
INTO ln_pallets_container;
EXIT WHEN ln_pallets%NOTFOUND;
SELECT param_value.param_value
INTO ln_days_till_expiration
FROM param_value
WHERE param_value.parameter_id = 266;
IF /*removed bracket*/ ln_pallets_container.recv_date >= trunc(SYSDATE - ln_days_till_expiration)
THEN --added
alarm.alarm(warehouse_id => my_commons.get_whrs_id,
source_text => ln_pallets_container.container_id,
message_code => 'Cannot find this container on warehouse. Check container code.');
END IF;
END LOOP;
CLOSE ln_pallets;
END check_stock_date;
然而,这可以更有效地完成。目前,您正在遍历wms_stock中的所有行,并且您正在明确地打开,提取并关闭游标。
这意味着对于wms_stock中的每一行,您都可以找到parameter_id 266的值(我假设您在循环播放结果时不会改变!),以及检查是否可以运行你的报警程序。
而不是获取所有的行,为什么不移动检查到游标 - 这样,你只会获取参数266值一次,并筛选出任何不需要运行警报程序的行。
与此同时,为什么不切换到使用循环游标?这样,您就不必担心打开/关闭/关闭游标,因为Oracle会为您处理所有这些问题。
这样做,这将导致在更短的代码,这恰好是更有效和更容易阅读和维护,就像这样:
PROCEDURE check_stock_date(warehouse_id_in IN warehouse.warehouse_id%TYPE) IS
BEGIN
FOR ln_pallets_rec IN (SELECT container_id,
recv_date
FROM wms_stock ws
WHERE recv_date >= (SELECT trunc(SYSDATE - param_value.param_value
FROM param_value
WHERE param_value.parameter_id = 266))
LOOP
alarm.alarm(warehouse_id => my_commons.get_whrs_id,
source_text => ln_pallets_rec.container_id,
message_code => 'Cannot find this container on warehouse. Check container code.');
END LOOP;
END check_stock_date;
答
嗨,你没有为ln_pallets_container变量也是它缺少指定表名“;”游标声明修复后,并尝试
答
修复了代码中的一些问题。
procedure check_stock_date(warehouse_id_in in warehouse.warehouse_id%type) is
ln_pallet_count number;
ln_days_till_expiration param_value.param_value%type;
l_container_id wms_stock.container_id%type;
l_recv_date wms_stock.recv_date%type;
cursor ln_pallets is
select container_id
,recv_date
from wms_stock ws;
begin
open ln_pallets;
loop
fetch ln_pallets
into l_container_id
,l_recv_date;
exit when ln_pallets%notfound;
select param_value.param_value
into ln_days_till_expiration
from param_value
where param_value.parameter_id = 266;
if l_recv_date >= trunc(sysdate - ln_days_till_expiration)
then
alarm.alarm(warehouse_id => my_commons.get_whrs_id
,source_text => l_container_id
,message_code => 'Cannot find this container on warehouse. Check container code.');
end if;
end loop;
close ln_pallets;
end;
看起来方式更好!正如你所说容易维护,但我有几个问题。 我不需要在这里声明任何东西?你只是使用了一些var ln_pallets_rec。稍后在LOOP中,source_text是我们想要使用的ln_pallets_rec.container_id,我不应该告诉哪种类型是ln_pallets_rec? –
[cursor-for-loop](https://docs.oracle.com/cloud/latest/db112/LNPLS/cursor_for_loop_statement.htm#LNPLS1155)的美妙之处在于Oracle会处理记录声明(有点像是什么时候你在1..10循环中做'我...'你不必声明i变量)加上光标处理。我更新了我的答案,以引用循环内正确的记录名称。值得注意的是,你可以显式声明游标,然后在for中引用游标名称,如下所示:'declare cursor my_cur as ...;在my_cur循环中开始my_rec ...,如果你想的话。 – Boneist
此外,隐式定义在cursor-for-loop中的记录范围纯粹是循环的,因此如果需要在循环外部引用它(例如,出于错误日志记录目的),则需要将其存储到变量中你有预先定义的,例如'声明my_var数字;开始for my_rec in(select ....)循环my_var:= my_rec.num_col; .... end loop;当其他人异常log_error(my_var);' – Boneist