【问题标题】:Best practice to count rows to prove feedback before processing them在处理之前计算行以证明反馈的最佳实践
【发布时间】:2019-03-04 14:11:18
【问题描述】:

我正在寻找在实际使用之前计算要用于某些微积分的行数的最佳方法。原因是我需要证明“已分析 1/100 行”、“已分析 2/100 行”等反馈。

我说的是 7000 行,执行不同计数的时间为 2 分钟。

最糟糕的是,现在我必须使用查询来获取计数,然后打开一个与上一个完全相同的游标(除了计数)并开始分析,所以代码是重复,每次我需要修改 where 子句时,我都必须修改两次。

我想把我的光标作为一个局部变量然后在 pl/sql 中运行一个 while 循环来增加一个计数器,但我不确定性能,所以这就是我问你一些建议的原因。

真的希望答案不会是使用包含总行数的已打开游标的某些神奇属性,因为我会觉得很愚蠢:(

除非经过验证的性能提升,否则我宁愿不使用 count(*) over (partition by),因为我认为乍一看太难理解。

谢谢大家

【问题讨论】:

  • 我会在临时表 ID 中选择您需要的所有行。然后您可以计算它们并用于其他计算。
  • 听起来你是在一个循环中一次处理一个行,所以我不确定你关心 PL/SQL 循环方法的开销。一个分析count 是做到这一点的方法,不知道这有什么困难或为什么重要。或者填充临时表或集合(对于小卷,因为这是共享服务器内存)。我不确定什么样的答案会有所帮助。
  • 事实上,我只想将真正需要所有迭代的代码放入循环中。使用count(*) over() 我必须插入一个if iteration=0 then write "there are **n** records to evaluate",在我的环境中,这个简单的行意味着10 行代码(不认为我需要解释原因)。现在我认为最好的做法是使用count
  • 什么版本的Oracle?为版本添加标签。如果使用 Oracle 12.1 或更高版本,如果不需要精确计数,您可以将count(*) 的性能提高APPROX_COUNT_DISTINCT(manager_id);在这种情况下,这个函数看起来像“大约有 x 行”。

标签: sql oracle performance plsql oracle11g


【解决方案1】:

你可以使用COUNT(*) OVER():

DECLARE
  CURSOR my_cur IS
  SELECT t.c, COUNT(*) OVER() AS total FROM tab t;

  v_c INT;
  v_total_rows INT;
  v_i INT :=0;
BEGIN
  OPEN my_cur;

  LOOP
    EXIT WHEN my_cur%NOTFOUND;
    FETCH my_cur INTO v_c, v_total_rows;

    IF v_i = 0 THEN
      DBMS_OUTPUT.put_line('Total number of rows to process: ' || v_total_rows);
    END IF;

    DBMS_OUTPUT.PUT_LINE('Current value :' || v_c);

    DBMS_OUTPUT.PUT_LINE('Progress: ' || 100.0 * v_i / v_total_rows || '%');
    v_i := v_i + 1;
  END LOOP;
  CLOSE my_cur;
END;
//

db<>fiddle demo


您可以使用SET_SESSION_LONGOPS,而不是打印到DBMS_OUTPUT

【讨论】:

  • 是的,但如果可能的话我宁愿不使用分析函数,反正你认为这个查询有很好的性能?
  • @VincenzoRaimondi 永远不要把任何事情视为理所当然。这是一个非常简单的玩具场景。您应该在您的环境中测量和检查解决方案。
  • 是的,我做到了,查询运行良好,唯一的问题是我必须写一个顶行,例如“准备好 v_total_rows 行”。这意味着我必须在循环中插入一个if v_i = 0 then put_line(v_total_rows) end if; ..我宁愿避免这种情况,因为我认为它会损害代码的易读性..也许我错了
  • DBMS_OUTPUT 在 PL/SQL 块完成之前不会显示,因此使用可由单独进程监控的 dbms_application_info.set_session_longops 会是更好的方法。
  • @MarkStewart 还必须记录其他信息,除了我肯定会考虑的 dbms_application_info.set_session_longops 之外,暂时将日志插入具有自主事务的专用表中
【解决方案2】:

如果需要知道一张表的完整行数,可以使用数据字典的num_rows(前提是统计是最新的):

CREATE TABLE mytest AS SELECT * FROM all_objects;

BEGIN dbms_stats.gather_table_stats(user, 'MYTEST'); END;
/

SELECT num_rows FROM user_tables WHERE table_name='MYTEST';
-- 60715

如果您的查询比较复杂并且会过滤掉行或执行其他花哨的事情,并且如果对预期行数的估计足够好,您可以使用 Oracle 的内置查询计划器。它需要弄清楚需要多少行才能找到查询它的最佳方式:

EXPLAIN PLAN SET STATEMENT_ID = 'st1-seq' FOR 
  SELECT * FROM mytest WHERE object_type = 'SEQUENCE';
SELECT cardinality from plan_table WHERE statement_id='st1-seq' and operation='SELECT STATEMENT';
10

EXPLAIN PLAN SET STATEMENT_ID = 'st1-java' FOR 
  SELECT * FROM mytest WHERE object_type = 'JAVA CLASS';
SELECT cardinality from plan_table WHERE statement_id='st1-java' and operation='SELECT STATEMENT';
37563 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-25
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    相关资源
    最近更新 更多