如何查找 Oracle PL/SQL 游标中的记录数?

发布于 2024-08-22 11:24:03 字数 421 浏览 5 评论 0原文

这是我的光标:

CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;

我立即打开光标,以便在程序期间锁定这些记录。

我想在存在 << 的情况下提出应用程序错误我的光标中有 2 条记录。使用 C1%ROWCOUNT 属性会失败,因为它只计算迄今为止已获取的数量。

该用例的最佳模式是什么?我是否需要创建一个虚拟 MY_TABLE%ROWTYPE 变量,然后循环游标以将它们取出并保持计数,或者是否有更简单的方法?如果这是这样做的方法,那么获取游标中的所有行会隐式关闭它,从而解锁这些行,或者即使我已经获取了所有行,它也会保持打开状态直到我显式关闭它吗?

我需要确保光标保持打开状态以执行超出此计数的各种其他任务。

Here's my cursor:

CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;

I immediately open the cursor in order to lock these records for the duration of my procedure.

I want to raise an application error in the event that there are < 2 records in my cursor. Using the C1%ROWCOUNT property fails because it only counts the number which have been fetched thus far.

What is the best pattern for this use case? Do I need to create a dummy MY_TABLE%ROWTYPE variable and then loop through the cursor to fetch them out and keep a count, or is there a simpler way? If this is the way to do it, will fetching all rows in my cursor implicitly close it, thus unlocking those rows, or will it stay open until I explicitly close it even if I've fetched them all?

I need to make sure the cursor stays open for a variety of other tasks beyond this count.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

我只土不豪 2024-08-29 11:24:03

注意:我刚刚重读了你的问题..如果只有 1 条记录,你想失败..
我稍后会发布新的更新..

让我们从这里开始..

来自 Oracle® Database PL/SQL 用户指南和参考
10g 版本 2 (10.2)
零件号 B14261-01
参考

所有行在您打开游标时都被锁定,而不是在获取它们时被锁定。当您提交或回滚事务时,这些行将被解锁。由于行不再被锁定,因此您无法在提交后从 FOR UPDATE 游标中获取数据。

所以您无需担心记录解锁。

所以试试这个..

declare 
  CURSOR mytable_cur IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;

  TYPE mytable_tt IS TABLE OF mytable_cur %ROWTYPE
    INDEX BY PLS_INTEGER;

  l_my_table_recs mytable_tt;
  l_totalcount NUMBER;
begin

   OPEN mytable_cur ;
   l_totalcount := 0;

   LOOP
      FETCH mytable_cur 
      BULK COLLECT INTO l_my_table_recs LIMIT 100;

      l_totalcount := l_totalcount + NVL(l_my_table_recs.COUNT,0);

      --this is the check for only 1 row..
      EXIT WHEN l_totalcount < 2;

      FOR indx IN 1 .. l_my_table_recs.COUNT
      LOOP
         --process each record.. via l_my_table_recs (indx)

      END LOOP;

      EXIT WHEN mytable_cur%NOTFOUND;
   END LOOP;

   CLOSE mytable_cur ;
end;

替代答案
我从后面读到你的答案,并认为如果有超过 1 行,你想退出。不完全是一行。所以这是我之前的答案。

2 种简单方法仅检查 1 条记录。

选项 1 - 显式获取

declare 
  CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
  l_my_table_rec C1%rowtype;
  l_my_table_rec2 C1%rowtype;
begin

    open C1;
    fetch c1 into l_my_table_rec;

    if c1%NOTFOUND then
       --no data found
    end if;

    fetch c1 into l_my_table_rec2;
    if c1%FOUND THEN
      --i have more then 1 row
    end if;
    close c1;

  -- processing logic

end;

我希望您明白这一点。

选项 2 - 异常捕获

declare 
  CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
  l_my_table_rec C1%rowtype;
begin
  begin
    select * 
      from my_table
      into l_my_table_rec
     where salary < 50000
       for update;
  exception
    when too_many_rows then
      -- handle the exception where more than one row is returned
    when no_data_found then
      -- handle the exception where no rows are returned
    when others then raise;
  end;

  -- processing logic
end;

另外
请记住:使用显式游标..您可以从游标记录而不是原始表中%TYPE您的变量。

当查询中有联接时,这尤其有用。

另外,请记住,您可以使用 type 语句更新表中的行

UPDATE table_name
SET set_clause
WHERE CURRENT OF cursor_name;

,但只有在您没有“获取”第二行时,这才有效。.

有关游标 FOR 循环的更多信息..尝试
此处

NB: i just reread you question.. and you want to fail if there is ONLY 1 record..
i'll post a new update in a moment..

lets start here..

From Oracle® Database PL/SQL User's Guide and Reference
10g Release 2 (10.2)
Part Number B14261-01

reference

All rows are locked when you open the cursor, not as they are fetched. The rows are unlocked when you commit or roll back the transaction. Since the rows are no longer locked, you cannot fetch from a FOR UPDATE cursor after a commit.

so you do not need to worry about the records unlocking.

so try this..

declare 
  CURSOR mytable_cur IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;

  TYPE mytable_tt IS TABLE OF mytable_cur %ROWTYPE
    INDEX BY PLS_INTEGER;

  l_my_table_recs mytable_tt;
  l_totalcount NUMBER;
begin

   OPEN mytable_cur ;
   l_totalcount := 0;

   LOOP
      FETCH mytable_cur 
      BULK COLLECT INTO l_my_table_recs LIMIT 100;

      l_totalcount := l_totalcount + NVL(l_my_table_recs.COUNT,0);

      --this is the check for only 1 row..
      EXIT WHEN l_totalcount < 2;

      FOR indx IN 1 .. l_my_table_recs.COUNT
      LOOP
         --process each record.. via l_my_table_recs (indx)

      END LOOP;

      EXIT WHEN mytable_cur%NOTFOUND;
   END LOOP;

   CLOSE mytable_cur ;
end;

ALTERNATE ANSWER
I read you answer backwards to start and thought you wanted to exit if there was MORE then 1 row.. not exactly one.. so here is my previous answer.

2 simple ways to check for ONLY 1 record.

Option 1 - Explicit Fetchs

declare 
  CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
  l_my_table_rec C1%rowtype;
  l_my_table_rec2 C1%rowtype;
begin

    open C1;
    fetch c1 into l_my_table_rec;

    if c1%NOTFOUND then
       --no data found
    end if;

    fetch c1 into l_my_table_rec2;
    if c1%FOUND THEN
      --i have more then 1 row
    end if;
    close c1;

  -- processing logic

end;

I hope you get the idea.

Option 2 - Exception Catching

declare 
  CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
  l_my_table_rec C1%rowtype;
begin
  begin
    select * 
      from my_table
      into l_my_table_rec
     where salary < 50000
       for update;
  exception
    when too_many_rows then
      -- handle the exception where more than one row is returned
    when no_data_found then
      -- handle the exception where no rows are returned
    when others then raise;
  end;

  -- processing logic
end;

Additionally
Remember: with an explicit cursor.. you can %TYPE your variable off the cursor record rather then the original table.

this is especially useful when you have joins in your query.

Also, rememebr you can update the rows in the table with an

UPDATE table_name
SET set_clause
WHERE CURRENT OF cursor_name;

type statement, but I that will only work if you haven't 'fetched' the 2nd row..

for some more information about cursor FOR loops.. try
Here

抠脚大汉 2024-08-29 11:24:03

如果您希望在返回超过 1 行时失败,请尝试以下操作:

declare 
  l_my_table_rec my_table%rowtype;
begin
  begin
    select * 
      from my_table
      into l_my_table_rec
     where salary < 50000
       for update;
  exception
    when too_many_rows then
      -- handle the exception where more than one row is returned
    when no_data_found then
      -- handle the exception where no rows are returned
    when others then raise;
  end;

  -- processing logic
end;

If you're looking to fail whenver you have more than 1 row returned, try this:

declare 
  l_my_table_rec my_table%rowtype;
begin
  begin
    select * 
      from my_table
      into l_my_table_rec
     where salary < 50000
       for update;
  exception
    when too_many_rows then
      -- handle the exception where more than one row is returned
    when no_data_found then
      -- handle the exception where no rows are returned
    when others then raise;
  end;

  -- processing logic
end;
等风来 2024-08-29 11:24:03

如果这是这样做的方法,将
获取光标中的所有行
隐式关闭它,从而解锁
这些行

锁将在事务持续时间内存在(即直到您执行提交或回滚),无论您何时(或是否)关闭游标。

我会选择

declare
  CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;;
  v_1 c1%rowtype;
  v_cnt number;
begin
  open c_1;
  select count(*) into v_cnt FROM MY_TABLE WHERE SALARY < 50000 and rownum < 3;
  if v_cnt < 2 then
    raise_application_error(-20001,'...');
  end if;
  --other processing
  close c_1;
end;

在打开游标(锁定行)和​​选择计数之间,有人将工资低于 50000 的一行或多行插入到表中的可能性很小。在这种情况下,应用程序错误将被引发,但游标只会处理打开游标时出现的行。如果这是一个担心,最后对 c_1%rowcount 进行另一次检查,如果遇到该问题,则需要回滚到保存点。

If this is the way to do it, will
fetching all rows in my cursor
implicitly close it, thus unlocking
those rows

The locks will be present for the duration of the transaction (ie until you do a commit or rollback) irrespective of when (or whether) you close the cursor.

I'd go for

declare
  CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;;
  v_1 c1%rowtype;
  v_cnt number;
begin
  open c_1;
  select count(*) into v_cnt FROM MY_TABLE WHERE SALARY < 50000 and rownum < 3;
  if v_cnt < 2 then
    raise_application_error(-20001,'...');
  end if;
  --other processing
  close c_1;
end;

There's a very small chance that, between the time the cursor is opened (locking rows) and the select count, someone inserts one or more rows into the table with a salary under 50000. In that case the application error would be raised but the cursor would only process the rows present when the cursor was opened. If that is a worry, at the end do another check on c_1%rowcount and, if that problem was experienced, you'd need to rollback to a savepoint.

萌能量女王 2024-08-29 11:24:03

在遍历游标之前创建一个保存点,然后在发现有 << 时使用部分回滚。返回 2 条记录。

Create a savepoint before you iterate through the cursor and then use a partial rollback when you find there are < 2 records returned.

空城旧梦 2024-08-29 11:24:03

您可以启动事务并检查是否 SELECT COUNT(*) MY_TABLE WHERE SALARY < 50000 大于 1。

You can start transaction and check if SELECT COUNT(*) MY_TABLE WHERE SALARY < 50000 greater than 1.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文