重构 PL/SQL:许多具有相同行类型的游标(理论上)

发布于 2024-11-07 14:47:58 字数 1702 浏览 3 评论 0原文

我正在编写一些 PL/SQL,发现自己陷入了重复模式:

cursor c_curs1 is
   select a, b, c
   from (...) big_subquery_1
   where big_subquery_1.a_ind = 'Y'

cursor c_curs2 is
   select a, b, c
   from (...) big_subquery_2
   where big_subquery_2.b_ind = 'R'

cursor c_curs3 is
   select a, b, c
   from (...) big_subquery_3
   where big_subquery_3.c_ind = 'M'

...
type t_curs1_tab is table of c_curs1;
type t_curs2_tab is table of c_curs2;
type t_curs3_tab is table of c_curs3;
...
v_curs1_results t_curs1_tab := t_curs1_tab();
v_curs2_results t_curs2_tab := t_curs2_tab();
v_curs3_results t_curs3_tab := t_curs3_tab();

然后在处理结果时,我有这样的代码:

    open c_curs1;
    fetch c_curs1 bulk collect into v_curs1_results;
    close c_curs1;

    if v_curs1_results.first is not null and v_curs1_results.last is not null then
       for i in v_curs1_results.first .. v_curs1_results.last loop
           /*Do something with field a in the results
             Do something with field b in the results
             Do something with field c in the results*/
           ....
       end loop;
    end if;

处理循环中的代码对于所有游标都是相同的,因为所有 3 个游标都返回 a ,b,c - 唯一的区别是引用哪个光标。我想将其重构为某种通用结果集处理器,但我被困在这里:

procedure sp_process_collection(in_collection t_curs1_tab) is ...

我只能用 v_curs1_results 调用它,不能用 v_curs2_results 调用它code> 或我收到 PLS-00306 类型或参数数量错误... 编译器错误。有没有什么办法可以通用地做到这一点,这样我只需要编写一个集合处理过程?我有这种光标模式(返回相同的三列,始终相同的类型)出现在同一包和处理循环的其他几个部分中,而语义上相同的有时用稍微不同的代码编写。我非常想将处理集中在一个过程中,我只是知道如何在 PL/SQL 中做到这一点。我知道 PL/SQL 没有泛型(我认为这会使 Java/C# 解决方案变得相当微不足道),但我想知道是否有另一种方法可以解决这个我刚刚没有的问题没想到。

(使用Oracle 10g)

I am writing some PL/SQL and found myself falling into a repeptitive pattern:

cursor c_curs1 is
   select a, b, c
   from (...) big_subquery_1
   where big_subquery_1.a_ind = 'Y'

cursor c_curs2 is
   select a, b, c
   from (...) big_subquery_2
   where big_subquery_2.b_ind = 'R'

cursor c_curs3 is
   select a, b, c
   from (...) big_subquery_3
   where big_subquery_3.c_ind = 'M'

...
type t_curs1_tab is table of c_curs1;
type t_curs2_tab is table of c_curs2;
type t_curs3_tab is table of c_curs3;
...
v_curs1_results t_curs1_tab := t_curs1_tab();
v_curs2_results t_curs2_tab := t_curs2_tab();
v_curs3_results t_curs3_tab := t_curs3_tab();

Then when processing the results, I have code like this:

    open c_curs1;
    fetch c_curs1 bulk collect into v_curs1_results;
    close c_curs1;

    if v_curs1_results.first is not null and v_curs1_results.last is not null then
       for i in v_curs1_results.first .. v_curs1_results.last loop
           /*Do something with field a in the results
             Do something with field b in the results
             Do something with field c in the results*/
           ....
       end loop;
    end if;

The code in the processing loop is the same for all cursors, as all 3 cursors return a,b,c - the only difference being which cursor is referenced. I wanted to refactor this into some sort of generic result-set processor, but I'm stuck here:

procedure sp_process_collection(in_collection t_curs1_tab) is ...

I can only call this with v_curs1_results, I can't call it with v_curs2_results or I get a PLS-00306 wrong number of types or arguments... compiler error. Is there any way to do this generically so I only have to write one collection processing procedure? I have this pattern of cursor (returning the same three columns, always the same types) appears in several other parts of the same package, and the processing loop, while semantically the same is sometimes written with slightly different code. I'd very much like to centralize the processing in one procedure, I just can'd figure out how to do it in PL/SQL. I know PL/SQL doesn't have generics (Which I think would have made a Java/C# solution fairly trivial), but I'm wondering if there's another way to approach this problem that I just haven't thought of.

(using Oracle 10g)

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

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

发布评论

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

评论(4

不爱素颜 2024-11-14 14:47:58

如果结果列相同,则没有理由使用三个 TYPE 声明。你只有一种类型。您是否需要该类型的一个或多个变量取决于您是否需要同时保存不同的数据集。

cursor c_curs1 is
   select a, b, c
   from (...) big_subquery_1
   where big_subquery_1.a_ind = 'Y'

cursor c_curs2 is
   select a, b, c
   from (...) big_subquery_2
   where big_subquery_2.b_ind = 'R'

cursor c_curs3 is
   select a, b, c
   from (...) big_subquery_3
   where big_subquery_3.c_ind = 'M'

...
type t_curs_tab is table of c_curs1;
...
v_curs_results t_curs_tab := t_curs_tab();
...
open c_curs1;
fetch c_curs1 bulk collect into v_curs_results;
close c_curs1;
...
open c_curs2;
fetch c_curs2 bulk collect into v_curs_results;
close c_curs2;
...
open c_curs3;
fetch c_curs3 bulk collect into v_curs_results;
close c_curs3;

If the resulting columns are the same, there's no reason to have three TYPE declarations. You just have one TYPE. Whether you need one or more variables of that TYPE depends on whether you need to hold the different data sets concurrently.

cursor c_curs1 is
   select a, b, c
   from (...) big_subquery_1
   where big_subquery_1.a_ind = 'Y'

cursor c_curs2 is
   select a, b, c
   from (...) big_subquery_2
   where big_subquery_2.b_ind = 'R'

cursor c_curs3 is
   select a, b, c
   from (...) big_subquery_3
   where big_subquery_3.c_ind = 'M'

...
type t_curs_tab is table of c_curs1;
...
v_curs_results t_curs_tab := t_curs_tab();
...
open c_curs1;
fetch c_curs1 bulk collect into v_curs_results;
close c_curs1;
...
open c_curs2;
fetch c_curs2 bulk collect into v_curs_results;
close c_curs2;
...
open c_curs3;
fetch c_curs3 bulk collect into v_curs_results;
close c_curs3;
请止步禁区 2024-11-14 14:47:58

如果您愿意并且被允许这样做,您可以创建一个 TYPE 来表示任何游标返回的记录。例如:

CREATE TYPE TRIPLE_TYPE IS OBJECT
(
  a NUMBER,
  b NUMBER,
  c NUMBER
);

还有另一种类型来保存 TRIPLE_TYPE 的集合:

CREATE TYPE TRIPLES_TYPE IS TABLE OF TRIPLE_TYPE;

最后,定义游标以返回 TRIPLE_TYPE 记录:

DECLARE

  cursor c_curs1 is
    select TRIPLE_TYPE(a, b, c)
    from (...) big_subquery_1
    where big_subquery_1.a_ind = 'Y';

  triples TRIPLES_TYPE;

BEGIN
  OPEN c_curs1;
  FETCH c_curs1 BULK COLLECT INTO triples;
  CLOSE c_curs1;
END;

此技术允许您编写一个接受 TRIPLE_TYPE 的过程。代码>TRIPLES_TYPE集合:

procedure sp_process_collection(in_collection TRIPLES_TYPE) is ...

If you are willing and permitted to do so, you can create a TYPE to represent a record returned by any of the cursors. For example:

CREATE TYPE TRIPLE_TYPE IS OBJECT
(
  a NUMBER,
  b NUMBER,
  c NUMBER
);

And another type to hold a collection of TRIPLE_TYPE:

CREATE TYPE TRIPLES_TYPE IS TABLE OF TRIPLE_TYPE;

Finally, you define your cursors to return TRIPLE_TYPE records:

DECLARE

  cursor c_curs1 is
    select TRIPLE_TYPE(a, b, c)
    from (...) big_subquery_1
    where big_subquery_1.a_ind = 'Y';

  triples TRIPLES_TYPE;

BEGIN
  OPEN c_curs1;
  FETCH c_curs1 BULK COLLECT INTO triples;
  CLOSE c_curs1;
END;

This technique allows you to write a procedure that accepts a TRIPLES_TYPE collection:

procedure sp_process_collection(in_collection TRIPLES_TYPE) is ...
攒眉千度 2024-11-14 14:47:58

您可以使用 REF CURSOR 来完成此操作吗?

类似于:

CREATE PROCEDURE sp_process_cursor(in_cursor SYS_REFCURSOR)

/* you don't need to open the cursor, it's already open */
FETCH in_cursor BULK COLLECT INTO v_cursor_results;
IF v_cursor_results.first is not null and v_cursor_results.last IS NOT NULL THEN
  FOR i in v_cursor_results.FIRST .. v_cursor_results.LAST LOOP
  ...
  END LOOP;
END IF;

您的调用代码需要每个游标调用此过程一次:

OPEN c_curs1;
sp_process_cursor(c_curs1);
CLOSE c_curs1;

OPEN c_curs2;
sp_process_cursor(c_curs2);
...

Can you do this with a REF CURSOR?

Something like:

CREATE PROCEDURE sp_process_cursor(in_cursor SYS_REFCURSOR)

/* you don't need to open the cursor, it's already open */
FETCH in_cursor BULK COLLECT INTO v_cursor_results;
IF v_cursor_results.first is not null and v_cursor_results.last IS NOT NULL THEN
  FOR i in v_cursor_results.FIRST .. v_cursor_results.LAST LOOP
  ...
  END LOOP;
END IF;

Your calling code would need to invoke this procedure once per cursor:

OPEN c_curs1;
sp_process_cursor(c_curs1);
CLOSE c_curs1;

OPEN c_curs2;
sp_process_cursor(c_curs2);
...
不必在意 2024-11-14 14:47:58

看起来你必须有 3 个游标,因为我认为每个游标中的“big_subquery”都是不同的。因此,您实际上只需要集中一个过程来处理 a、b 和 c 结果。这里没什么太花哨的,只需定义一个本地过程来执行此操作:

CREATE or replace procedure my_proc IS

  cursor c_curs1 is
   select a, b, c
   from (...) big_subquery_1
   where big_subquery_1.a_ind = 'Y';

  cursor c_curs2 is
   select a, b, c
   from (...) big_subquery_2
   where big_subquery_2.b_ind = 'R';

  cursor c_curs3 is
   select a, b, c
   from (...) big_subquery_3
   where big_subquery_3.c_ind = 'M';

  -- local procedure to handle processing of a,b,c "types"
  procedure process_type(i_type in varchar2) is
  begin
    -- do something great here
  end process_type;

BEGIN
  for rec in c_curs1
  loop
    process_type(rec.a);
    -- do something else
    process_type(rec.b);
    -- do something more
    process_type(rec.c);
    -- do something even more
  end loop;

  for rec in c_curs2
  loop
    -- do processing for this cursor
    -- use process_type like above
  end loop;

  for rec in c_curs3
  loop
    -- do processing for this cursor
    -- use process_type like above
  end loop;

END;

当然,如果您需要为每行一起处理所有 3 种类型,或者类型不同(varchar2、日期和数字),只需修改 process_type 过程传递所有 3 个(a、b 和 c)。例如:

procedure process_type(i_type_a in varchar2, i_type_b in date, i_type_c in number) is
  begin
    -- do something great here
  end process_type;

如果您需要返回值,您可以将其设为函数。

Seems like you HAVE to have 3 cursors because the "big_subquery" in each are different I assume. So, you really just need to centralize a procedure to handle processing a, b, and c results. Nothing too fancy here, just define a local procedure to do this:

CREATE or replace procedure my_proc IS

  cursor c_curs1 is
   select a, b, c
   from (...) big_subquery_1
   where big_subquery_1.a_ind = 'Y';

  cursor c_curs2 is
   select a, b, c
   from (...) big_subquery_2
   where big_subquery_2.b_ind = 'R';

  cursor c_curs3 is
   select a, b, c
   from (...) big_subquery_3
   where big_subquery_3.c_ind = 'M';

  -- local procedure to handle processing of a,b,c "types"
  procedure process_type(i_type in varchar2) is
  begin
    -- do something great here
  end process_type;

BEGIN
  for rec in c_curs1
  loop
    process_type(rec.a);
    -- do something else
    process_type(rec.b);
    -- do something more
    process_type(rec.c);
    -- do something even more
  end loop;

  for rec in c_curs2
  loop
    -- do processing for this cursor
    -- use process_type like above
  end loop;

  for rec in c_curs3
  loop
    -- do processing for this cursor
    -- use process_type like above
  end loop;

END;

Of course, if you need to process all 3 types together for each row, or if the types are different (varchar2, date, and number), just modify the process_type procedure to pass in all 3 (a,b,and c). Something like:

procedure process_type(i_type_a in varchar2, i_type_b in date, i_type_c in number) is
  begin
    -- do something great here
  end process_type;

And you can make this a function if you need a return value.

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