PL/SQL 游标:将计数提取为整数,语法错误?

Posted

技术标签:

【中文标题】PL/SQL 游标:将计数提取为整数,语法错误?【英文标题】:PL/SQL cursor: Fetch count into an integer, bad syntax? 【发布时间】:2013-10-24 13:07:22 【问题描述】:

编辑:请参阅下面的完整代码编辑,第一个有效。这可能与标题中的内容不同。

我正在尝试将游标的计数结果放入一个整数,但该整数始终保持为 0。

DECLARE    
   v_count int := 0;

CURSOR logins IS
   SELECT count(*)
   FROM big_table;

BEGIN
OPEN logins;
FETCH logins into v_count;

IF v_count > 10 THEN
   DBMS_OUTPUT.PUT_LINE ('Hi mom');
END IF;

CLOSE logins;
END;

这只是我想要执行此操作的代码示例,此获取实际上是在一个循环中。

我尝试在 IF 语句之前添加“SELECT count(*) INTO v_count...”。它可以工作,但速度非常慢。

谢谢!

编辑:正如 A.B.Cade 所指出的,此示例有效。所以我的问题可能在我的代码中的其他地方,你会在下面找到整个事情:

DECLARE
    v_hour int := 0;
    v_maxConn int;
    v_count int;
    --set the time parameters
    --v_day = the first day you loop in
    v_day int := 16;
    v_month varchar2 (2) := 10;
    v_year varchar2 (4) := 2013;
    v_lastDay int := 16;

CURSOR logins IS

        SELECT count(*)
        FROM (
            SELECT (SELECT user_function_name
            FROM apps.fnd_form_functions_vl fffv
            WHERE (fffv.function_id = a.function_id)) "Current Function",
               first_connect,
               last_connect,
               user_name, session_id,
               apps.fnd_profile.value_specific
                                    ('ICX_SESSION_TIMEOUT',
                                     a.user_id,
                                     a.responsibility_id,
                                     a.responsibility_application_id,
                                     a.org_id,
                                     NULL
                                    ) TIMEOUT,
               counter "How many hits a User has made",
               a.limit_connects "No of hits allowed in session"
            FROM icx.icx_sessions a, fnd_user b
            WHERE a.user_id = b.user_id
            AND last_connect > SYSDATE - 30
            AND b.user_name NOT LIKE 'GUEST'
             )
        WHERE to_date(v_year || '-' || v_month || '-' || v_day || ' ' || v_hour || ':00:00','YYYY-MM-DD HH24:MI:SS') between first_connect and last_connect
        ;

    out_menu varchar2(500);
    out_path varchar2(50) := '/usr/tmp/QA';
    file_out utl_file.file_type ;

BEGIN
file_out := utl_file.fopen(out_path,'debug_rapport.txt','W');
OPEN logins;

LOOP EXIT WHEN v_day > v_lastDay;
    v_maxConn := 0;

    LOOP EXIT WHEN v_hour > 23;

        FETCH logins into v_count;

        IF v_count > v_maxConn THEN
           v_maxConn := v_count;
        END IF;

        out_menu := 'Debug: ' || to_char(sysdate, 'YYYY-MM-DD HH24:MI:SS') || ' -> dateLoop: ' || to_date(v_year || '-' || v_month || '-' || v_day || ' ' || v_hour || ':00:00','YYYY-MM-DD HH24:MI:SS') || ' -> v_maxConn: ' || v_maxConn || ' -> hour:' || v_hour;
        utl_file.put_line(file_out,out_menu);

        v_hour := v_hour + 1;

    END LOOP;

    DBMS_OUTPUT.PUT_LINE (v_year || '-' || v_month || '-' || v_day || ';' || v_maxConn);

    v_hour := 0;
    v_day := v_day + 1;

END LOOP;

CLOSE logins;

END;

代码用于查看给定日期的每个小时内的并发连接。如果我用游标中的 SELECT 语句替换 BEGIN 语句中的游标代码,它会起作用。

这是我使用光标时“调试”文件的输出:

Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:0
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:1
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:2
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:3
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:4
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:5
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:6
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:7
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:8
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:9
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:10
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:11
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:12
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:13
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:14
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:15
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:16
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:17
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:18
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:19
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:20
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:21
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:22
Debug: 2013-10-24 10:19:32 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:23

这是我用“SELECT count(*) INTO v_count [...]”替换光标时的输出:

Debug: 2013-10-24 10:00:40 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:0
Debug: 2013-10-24 10:00:54 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:1
Debug: 2013-10-24 10:01:09 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:2
Debug: 2013-10-24 10:01:23 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:3
Debug: 2013-10-24 10:01:37 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:4
Debug: 2013-10-24 10:01:50 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:5
Debug: 2013-10-24 10:02:05 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:6
Debug: 2013-10-24 10:02:20 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:7
Debug: 2013-10-24 10:02:33 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:8
Debug: 2013-10-24 10:02:47 -> dateLoop: 13-10-16 -> v_maxConn: 0 -> hour:9
Debug: 2013-10-24 10:03:00 -> dateLoop: 13-10-16 -> v_maxConn: 1 -> hour:10
Debug: 2013-10-24 10:03:15 -> dateLoop: 13-10-16 -> v_maxConn: 1 -> hour:11
Debug: 2013-10-24 10:03:28 -> dateLoop: 13-10-16 -> v_maxConn: 1 -> hour:12
Debug: 2013-10-24 10:03:41 -> dateLoop: 13-10-16 -> v_maxConn: 1 -> hour:13
Debug: 2013-10-24 10:03:54 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:14
Debug: 2013-10-24 10:04:08 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:15
Debug: 2013-10-24 10:04:22 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:16
Debug: 2013-10-24 10:04:35 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:17
Debug: 2013-10-24 10:04:47 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:18
Debug: 2013-10-24 10:05:00 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:19
Debug: 2013-10-24 10:05:13 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:20
Debug: 2013-10-24 10:05:25 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:21
Debug: 2013-10-24 10:05:38 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:22
Debug: 2013-10-24 10:05:51 -> dateLoop: 13-10-16 -> v_maxConn: 2 -> hour:23

您看到 v_maxConn 没有保持为零,因此正在写入 v_count。不过,一天只需 5 分钟。

非常感谢您的帮助!

【问题讨论】:

这是您将计数存储在变量中的唯一方法,尽管它非常慢。 您的代码似乎可以正常工作sqlfiddle.com/#!4/07131/1(我已将其放入小提琴的函数中) 如果要检查 count(*)>10,则使用SELECT count(*) FROM bigtable WHERE rownum <= 11,此查询仅读取表中的 11 行而不是所有行,应该会执行得更好。 @kordirko,基本上你是对的,但正如 OP 所说 这只是我想要执行此操作的代码示例,此获取实际上是在一个循环中 ,我们真的不知道他想做什么 @A.B.Cade 感谢您指出该示例有效,我在实际数据库中进行了尝试,它确实有效。我用完整的查询编辑 OP。 【参考方案1】:

我稍微修改了你的代码,这行得通..

DECLARE    
   v_count int := 0;

CURSOR logins IS
   SELECT count(*)
   FROM big_table;

BEGIN

OPEN logins;

loop

FETCH logins into v_count;



IF v_count >= 10 THEN

   DBMS_OUTPUT.PUT_LINE ('Hi mom');

   exit;

END IF;

end loop;

CLOSE logins;

END;

你忘记了循环、结束循环和退出语句,否则会出现无限循环... 希望这可以帮助。如果少于 10 行,则为无限循环。

【讨论】:

谢谢,但我用完整的代码编辑了 OP。我制作的样本确实有效,所以问题不是我想的。【参考方案2】:

您正在做一件奇怪的事情 - 您正在循环 v_hourv_date,但您正在重新获取仅包含一条记录的同一个打开的游标。

所以基本上发生的事情是你一遍又一遍地得到相同的值(0)

Here is a sqlfiddle demo 的示例与您的情况非常相似。

OPEN logins;

for v_i in 1..10 loop
FETCH logins into v_count;

IF v_count > 10 THEN
   v_res := v_res ||v_i || '. v_count=' || v_count || ', ';
END IF;

end loop;
CLOSE logins;

如果你真的想这样做,那么你需要为光标添加一个参数并在每次迭代中重新打开它:

Here is another sample that works

for v_i in 1..10 loop
OPEN logins(v_i);
FETCH logins into v_count;

IF v_count > 10 THEN
   v_res := v_res ||v_i || '. v_count=' || v_count || ', ';
END IF;
CLOSE logins;
end loop;

但是,这就像在每次迭代中查询 count(*)(可能会变慢)

很难说,但看起来你可以在一个查询中获取值-

类似this

【讨论】:

SELECT 有一个 WHERE 子句,其变量 v_day 和 v_hour 随每次迭代而变化: WHERE to_date(v_year || '-' || v_month || '-' || v_day || ' ' || v_hour || ':00:00','YYYY-MM-DD HH24:MI:SS') 在 first_connect 和 last_connect 之间;那么,是否有可能我不能在不刷新光标的情况下使用它,因为我每次调用它时都会更改查询?除了 WHERE 子句之外,我使用的选择在查询期间永远不会改变。我想我将创建一个视图或一个表来在代码期间保存此查询并进行查询。我会更新解决方案。 否...当您打开游标时,您执行了一个查询并得到了一个结果,当您对其进行迭代时,它是相同的结果,除非您通过显式运行重新查询,否则它不会改变查询或通过重新打开并获取游标。您应该寻找一种方法来在一个查询中获取所有结果(对于每个 v_day 和 v_hour)。 @davidb,也许您可​​以打开另一个帖子并描述您的表结构并要求提供所有结果的查询【参考方案3】:

问题是我没有完全理解游标是如何工作的。我用一个临时表替换了游标,在其中放置了我的静态查询:

CREATE TABLE apps.audit_custom as
select [...]

然后我在我的 LOOP 中查询这个表,如下所示:

SELECT count(*) into v_count
        FROM apps.audit_custom
        WHERE to_date(v_year || '-' || v_month || '-' || v_day || ' ' || v_hour || ':00:00','YYYY-MM-DD HH24:MI:SS') between first_connect and last_connect;

最后,我完成后放下桌子。

这是我发现实现想法的最快方法。

感谢大家的帮助!

【讨论】:

以上是关于PL/SQL 游标:将计数提取为整数,语法错误?的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL:语法错误

游标问题和语法 PLSQL

在 PL/SQL 中将游标数据提取到数组中

PL/SQL - 你可以通过索引访问游标中的某些记录吗?

PL/SQL 编程游标存储过程函数

选择查询中的 PL/SQL 存储错误