sql - 当表中的记录数是指定限制数的倍数时,如何使用 ref 游标获取集合?
问题描述
创建带有 id 列的表
CREATE TABLE TEST_TAB
(ID NUMBER
);
创建类型
CREATE type numbertabletype IS TABLE OF NUMBER;
插入 100 条记录
INSERT INTO TEST_TAB
SELECT LL FROM
(SELECT LEVEL LL FROM DUAL CONNECT BY LEVEL<=100
);
创建在 10 次循环中遍历 100 条记录的函数,游标将获取到具有 LIMIT 10 的数字表类型集合。
CREATE OR REPLACE FUNCTION LOOP_TEST
RETURN NUMBERTABLETYPE
IS
lv_coll NUMBERTABLETYPE ;
LV_COUNT NUMBER:=0;
CURSOR c1
IS
SELECT ID FROM TEST_TAB WHERE ROWNUM<=100;
BEGIN
OPEN c1;
LOOP
dbms_output.put_line('BEFORE FETCH CURSOR COUNT '||C1%ROWCOUNT);
FETCH c1 bulk collect INTO lv_coll limit 10;
dbms_output.put_line('AFTER FETCH CURSOR COUNT '||C1%ROWCOUNT);
EXIT
WHEN c1%NOTFOUND;
LV_COUNT:=LV_COUNT+1;
dbms_output.put_line(' BELOW NOT FOUND '||LV_COUNT);
dbms_output.put_line('COLLECTION COUNT '||lv_coll.count);
END LOOP;
CLOSE c1;
RETURN lv_coll;
END;
/
将sql作为脚本运行,rownum = 100时返回null
CURSOR c1
IS
SELECT ID FROM TEST_TAB WHERE ROWNUM<=100;
将sql作为脚本运行,当rownum = 99时返回值,将函数中的光标c1替换为下面的光标。
CURSOR c1
IS
SELECT ID FROM TEST_TAB WHERE ROWNUM<=99;
那么当限制是要循环的总记录的倍数时,您如何处理这种情况。
解决方案
代码运行良好;它只是没有做你期望的事情。
当代码循环时,它将lv_coll
每次覆盖集合,使其包含不超过 10 个项目。
当它获取第 91-100 行时,它会用 10 个项目填充集合并处理它,但是由于它没有尝试读取另一行,它不知道游标中没有更多行并且尚未达到c1%NOTFOUND
条件终止循环。
当它重复循环之后,它会发现游标现在已经用尽,将读取零行。因此,在最后一个循环中,lv_coll
集合不是NULL
,而是包含零个元素的集合,这就是将要返回的内容。
将此与游标中只有 99 行的情况进行比较。当循环尝试读取第 91 - 第 100 行时,它将读取第 91 - 第 99 行并尝试读取第 100 行,但会发现游标已用尽,并且c1%NOTFOUND
将退出循环并导致集合仅返回9 项。
如果要从游标返回所有元素,则需要使用第二个集合来聚合它们,因为从游标填充的元素将在每个循环中被覆盖:
CREATE OR REPLACE FUNCTION LOOP_TEST
RETURN NUMBERTABLETYPE
IS
lv_coll NUMBERTABLETYPE;
all_items NUMBERTABLETYPE := NUMBERTABLETYPE();
LV_COUNT NUMBER:=0;
ids VARCHAR2(30);
CURSOR c1
IS
SELECT ID FROM TEST_TAB WHERE ROWNUM<=100;
BEGIN
OPEN c1;
LOOP
dbms_output.put_line('BEFORE FETCH CURSOR COUNT '||C1%ROWCOUNT);
FETCH c1 bulk collect INTO lv_coll limit 10;
dbms_output.put_line('AFTER FETCH CURSOR COUNT '||C1%ROWCOUNT);
EXIT WHEN c1%NOTFOUND;
LV_COUNT:=LV_COUNT+1;
all_items := all_items MULTISET UNION ALL lv_coll;
dbms_output.put_line(' BELOW NOT FOUND '||LV_COUNT);
dbms_output.put_line('COLLECTION COUNT '||lv_coll.count);
-- SELECT LISTAGG(COLUMN_VALUE,',') WITHIN GROUP( ORDER BY ROWNUM )
-- INTO ids
-- FROM TABLE(lv_coll);
-- dbms_output.put_line('IDS: '||ids);
END LOOP;
CLOSE c1;
RETURN all_items;
END;
/
可以使用以下方法调用:
DECLARE
ids numbertabletype;
vals VARCHAR2(3000);
BEGIN
ids := LOOP_TEST();
DBMS_OUTPUT.PUT_LINE( 'NUMBER OF IDs: ' || CASE WHEN ids IS NULL THEN 'NULL' ELSE TO_CHAR( ids.COUNT ) END );
SELECT LISTAGG(COLUMN_VALUE,',') WITHIN GROUP( ORDER BY ROWNUM )
INTO vals
FROM TABLE(ids);
DBMS_OUTPUT.PUT_LINE( 'values: ' || vals );
END;
/
db<>在这里摆弄
推荐阅读
- jquery - 单元格单击事件仅在剑道 UI 网格中发生一次
- php - 为什么 linux 中的 sudo nohup 总是打开两个相同的进程?
- symfony - bitbucket 上的私有 symfony 包,作曲家找不到 github 上的配方
- python - Telegram bot 获取发送消息的时间
- google-apps-script - 将序列号分配给 Google 表格中的重复项以按人订购表单回复
- scala - 在 UDF 正文中将列作为参数传递
- powershell - powershell 选择对象属性和扩展属性
- processing - 处理 P3D 动画,留下伪影
- sql - 使用触发器复制更新的行值
- macos - MTKView 的初始渲染倾斜