PL/SQL - 你能通过索引访问游标中的特定记录吗?
PL/SQL - Can you access certain record in a cursor by index?
我正在尝试编写测试,并给出了一组评论,我想将其中 1 设置为 'Status A' 和 all其余 至 'Status B'。我知道 PL/SQL,有一个 CURSOR 的 FOR LOOP 语法,我知道它可以处理所有评论,但是有没有办法访问该游标中的特定记录?
我认为可以解决我的问题的是一种按索引访问游标的方法,类似于在其他语言中按索引访问数组的方法。 PL/SQL有没有办法做到这一点?
我对PL/SQL和游标的语法还是很陌生,所以我会用伪代码写出我想做的事情。
伪代码
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
v_first_id reviewer_tbl.id%TYPE;
FOR i in 0..c_reviewer_ids.length
{
IF i == 0
{
v_first_id := c_reviewer_ids(i);
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = c_reviewer_ids(i);
}
ELSE
{
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE id = c_reviewer_ids(i);
}
}
我能够制作一个 CURSOR FOR LOOP,但它对每条记录的处理都相同,我只想对一条记录做一些特别的事情。这是我目前拥有的:
CURSOR FOR LOOP(不处理特殊情况)
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
v_reviewer_id reviewer_tbl.id%TYPE;
FOR l_reviewer_id IN c_reviewer_ids
LOOP
--Set the status for all reviewers.
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE reviewer_id = l_reviewer_id.id;
--Save one of the ids; for this particular test, it doesn't matter if it is first or not
v_reviewer_id := l_reviewer_id.id;
END LOOP;
您可以使用 BOOLEAN 来告诉您是否已处理游标中的 "first" 行,其方式类似于以下内容:
DECLARE
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
v_reviewer_id reviewer_tbl.id%TYPE;
bFirst_row BOOLEAN := TRUE;
BEGIN
FOR l_reviewer_id IN c_reviewer_ids
LOOP
IF bFirst_row THEN
bFirst_row := FALSE;
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = l_reviewer_id.ID;
ELSE
--Set the status for all other reviewers.
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE reviewer_id = l_reviewer_id.ID;
END IF;
--Save one of the ids; for this particular test, it doesn't matter if it is first or not
v_reviewer_id := l_reviewer_id.id;
END LOOP;
END;
祝你好运。
编辑
如果你想访问第五行,你可以这样做:
DECLARE
v_reviewer_id reviewer_tbl.id%TYPE;
BEGIN
FOR l_reviewer_id IN (SELECT id, RN
FROM (SELECT ID, ROWNUM AS RN
FROM reviewer_tbl
WHERE course_id = 123)
WHERE RN = 5)
LOOP
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = l_reviewer_id.ID;
--Save one of the ids; for this particular test, it doesn't matter if it is first or not
v_reviewer_id := l_reviewer_id.id;
END LOOP;
END;
祝你好运。
您可以使用 "BULK COLLECT INTO" 子句将所有查询结果加载到实际的内存数组中,然后使用常规的基于索引的访问来访问该数组。
这不是内存效率高的方法(因此请注意,如果您正在处理大量记录,则不应这样做),但它有效:
查看此示例,我在内存数组中加载 "DICT" 系统视图的前 100 条记录:
declare
type MEMTABLE_TYPE is
TABLE OF DICT%ROWTYPE index by binary_integer;
myarray MEMTABLE_TYPE;
begin
select *
BULK COLLECT INTO myarray -- this loads the whole query result into the array
from DICT
where rownum < 100;
-- scan all the array:
for c in 1..myarray.count loop
dbms_output.put_line(myarray(c).table_name ||' -> ' || myarray(c).comments);
end loop;
-- access directly the fifth element
dbms_output.put_line(myarray(5).table_name ||' -> ' || myarray(5).comments);
end;
无论如何你不应该滥用这个:除非你需要多次访问数据(所以将它保存在内存中而不是重新执行查询可以加快速度)你应该尝试使用常规游标。
使用 this from the Oracle documentation,我找到了一种使用 INDEX-BY
table 的方法,它最符合我在伪代码中想要的内容。它创建游标,然后将其放入临时 table,然后可以对其进行索引。
写完代码后,我意识到游标可能没有那么必要,因为它会立即关闭。在这种情况下,可以省略光标,只使用 INDEX BY
table(类似于 Carlo 在他的回答中所写的内容)。
与游标
一起使用索引table
DEFINE
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
--table type
TYPE reviewer_id_tbl_type IS TABLE OF c_reviewer_ids%ROWTYPE INDEX BY PLS_INTEGER;
--actual table
t_reviewer_ids reviewer_id_tbl_type;
v_first_id reviewer_tbl.id%TYPE;
BEGIN
--Fetch the cursor into the indexed table
OPEN c_reviewer_ids;
FETCH c_reviewer_ids BULK COLLECT INTO t_reviewer_ids;
CLOSE c_reviewer_ids; --cursor won't be used anymore
--For loop
FOR i IN 1..t_reviewer_ids.COUNT() LOOP
IF i = 1 THEN
v_first_id := t_reviewer_ids(i).id;
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = t_reviewer_ids(i).id;
ELSE
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE id = t_reviewer_ids(i).id;
END IF;
END LOOP;
END;
使用索引 table 代替游标
DEFINE
--table type
TYPE reviewer_id_tbl_type IS TABLE OF reviewer_tbl.id%TYPE INDEX BY PLS_INTEGER;
--actual table
t_reviewer_ids reviewer_id_tbl_type;
v_first_id reviewer_tbl.id%TYPE;
BEGIN
--Fetch the query into the indexed table
SELECT id
BULK COLLECT INTO t_reviewer_ids
FROM reviewer_tbl
WHERE course_id = 123;
--For loop; Notice that .id is not needed anymore
FOR i IN 1..t_reviewer_ids.COUNT() LOOP
IF i = 1 THEN
v_first_id := t_reviewer_ids(i);
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = t_reviewer_ids(i);
ELSE
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE id = t_reviewer_ids(i);
END IF;
END LOOP;
END;
我正在尝试编写测试,并给出了一组评论,我想将其中 1 设置为 'Status A' 和 all其余 至 'Status B'。我知道 PL/SQL,有一个 CURSOR 的 FOR LOOP 语法,我知道它可以处理所有评论,但是有没有办法访问该游标中的特定记录?
我认为可以解决我的问题的是一种按索引访问游标的方法,类似于在其他语言中按索引访问数组的方法。 PL/SQL有没有办法做到这一点?
我对PL/SQL和游标的语法还是很陌生,所以我会用伪代码写出我想做的事情。
伪代码
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
v_first_id reviewer_tbl.id%TYPE;
FOR i in 0..c_reviewer_ids.length
{
IF i == 0
{
v_first_id := c_reviewer_ids(i);
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = c_reviewer_ids(i);
}
ELSE
{
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE id = c_reviewer_ids(i);
}
}
我能够制作一个 CURSOR FOR LOOP,但它对每条记录的处理都相同,我只想对一条记录做一些特别的事情。这是我目前拥有的:
CURSOR FOR LOOP(不处理特殊情况)
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
v_reviewer_id reviewer_tbl.id%TYPE;
FOR l_reviewer_id IN c_reviewer_ids
LOOP
--Set the status for all reviewers.
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE reviewer_id = l_reviewer_id.id;
--Save one of the ids; for this particular test, it doesn't matter if it is first or not
v_reviewer_id := l_reviewer_id.id;
END LOOP;
您可以使用 BOOLEAN 来告诉您是否已处理游标中的 "first" 行,其方式类似于以下内容:
DECLARE
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
v_reviewer_id reviewer_tbl.id%TYPE;
bFirst_row BOOLEAN := TRUE;
BEGIN
FOR l_reviewer_id IN c_reviewer_ids
LOOP
IF bFirst_row THEN
bFirst_row := FALSE;
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = l_reviewer_id.ID;
ELSE
--Set the status for all other reviewers.
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE reviewer_id = l_reviewer_id.ID;
END IF;
--Save one of the ids; for this particular test, it doesn't matter if it is first or not
v_reviewer_id := l_reviewer_id.id;
END LOOP;
END;
祝你好运。
编辑
如果你想访问第五行,你可以这样做:
DECLARE
v_reviewer_id reviewer_tbl.id%TYPE;
BEGIN
FOR l_reviewer_id IN (SELECT id, RN
FROM (SELECT ID, ROWNUM AS RN
FROM reviewer_tbl
WHERE course_id = 123)
WHERE RN = 5)
LOOP
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = l_reviewer_id.ID;
--Save one of the ids; for this particular test, it doesn't matter if it is first or not
v_reviewer_id := l_reviewer_id.id;
END LOOP;
END;
祝你好运。
您可以使用 "BULK COLLECT INTO" 子句将所有查询结果加载到实际的内存数组中,然后使用常规的基于索引的访问来访问该数组。
这不是内存效率高的方法(因此请注意,如果您正在处理大量记录,则不应这样做),但它有效:
查看此示例,我在内存数组中加载 "DICT" 系统视图的前 100 条记录:
declare
type MEMTABLE_TYPE is
TABLE OF DICT%ROWTYPE index by binary_integer;
myarray MEMTABLE_TYPE;
begin
select *
BULK COLLECT INTO myarray -- this loads the whole query result into the array
from DICT
where rownum < 100;
-- scan all the array:
for c in 1..myarray.count loop
dbms_output.put_line(myarray(c).table_name ||' -> ' || myarray(c).comments);
end loop;
-- access directly the fifth element
dbms_output.put_line(myarray(5).table_name ||' -> ' || myarray(5).comments);
end;
无论如何你不应该滥用这个:除非你需要多次访问数据(所以将它保存在内存中而不是重新执行查询可以加快速度)你应该尝试使用常规游标。
使用 this from the Oracle documentation,我找到了一种使用 INDEX-BY
table 的方法,它最符合我在伪代码中想要的内容。它创建游标,然后将其放入临时 table,然后可以对其进行索引。
写完代码后,我意识到游标可能没有那么必要,因为它会立即关闭。在这种情况下,可以省略光标,只使用 INDEX BY
table(类似于 Carlo 在他的回答中所写的内容)。
与游标
一起使用索引tableDEFINE
CURSOR c_reviewer_ids IS
SELECT id FROM reviewer_tbl
WHERE course_id = 123;
--table type
TYPE reviewer_id_tbl_type IS TABLE OF c_reviewer_ids%ROWTYPE INDEX BY PLS_INTEGER;
--actual table
t_reviewer_ids reviewer_id_tbl_type;
v_first_id reviewer_tbl.id%TYPE;
BEGIN
--Fetch the cursor into the indexed table
OPEN c_reviewer_ids;
FETCH c_reviewer_ids BULK COLLECT INTO t_reviewer_ids;
CLOSE c_reviewer_ids; --cursor won't be used anymore
--For loop
FOR i IN 1..t_reviewer_ids.COUNT() LOOP
IF i = 1 THEN
v_first_id := t_reviewer_ids(i).id;
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = t_reviewer_ids(i).id;
ELSE
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE id = t_reviewer_ids(i).id;
END IF;
END LOOP;
END;
使用索引 table 代替游标
DEFINE
--table type
TYPE reviewer_id_tbl_type IS TABLE OF reviewer_tbl.id%TYPE INDEX BY PLS_INTEGER;
--actual table
t_reviewer_ids reviewer_id_tbl_type;
v_first_id reviewer_tbl.id%TYPE;
BEGIN
--Fetch the query into the indexed table
SELECT id
BULK COLLECT INTO t_reviewer_ids
FROM reviewer_tbl
WHERE course_id = 123;
--For loop; Notice that .id is not needed anymore
FOR i IN 1..t_reviewer_ids.COUNT() LOOP
IF i = 1 THEN
v_first_id := t_reviewer_ids(i);
UPDATE reviewer_tbl
SET status = 'Status A'
WHERE id = t_reviewer_ids(i);
ELSE
UPDATE reviewer_tbl
SET status = 'Status B'
WHERE id = t_reviewer_ids(i);
END IF;
END LOOP;
END;