【问题标题】:Get the count of each table获取每个表的计数
【发布时间】:2014-02-08 01:32:35
【问题描述】:

我试图创建一个过程来获取与模式对应的所有表的总行数。 我正在使用一个游标来存储表的总列表,并且同样会进一步迭代。即使功能未经测试。程序创建编译时出现以下错误。

create or replace
PROCEDURE PROC_TABLE_COUNT
AS
  table_count NUMBER;
  CURSOR total_tables
  IS
     SELECT table_name FROM dba_tables WHERE owner = 'OWNER_NAME';
BEGIN
  FOR i IN total_tables
  LOOP
  SELECT COUNT (*) INTO table_count FROM dba_tables db where db.table_name = i.table_name;
  END LOOP;
END PROC_TABLE_COUNT;


1)Error(7,6): PL/SQL: SQL Statement ignored
2)Error(7,33): PL/SQL: ORA-00942: table or view does not exist
3)Error(11,1): PL/SQL: SQL Statement ignored
4)Error(11,76): PL/SQL: ORA-00904: "I"."TABLE_NAME": invalid identifier
5)Error(11,76): PLS-00364: loop index variable 'I' use is invalid

问题 1: 错误 2(dba_tables)是否是由于授权被拒绝?通过右键单击过程名称,我尝试分配调试和执行的权限。但错误仍然存​​在。 问题2: 关于无效标识符。为什么会出现这个错误?

更新:

根据有价值的评论之一,我更改了查询并且编译错误消失了。现在逻辑有问题。

create or replace procedure proc_tab_count as 
  table_count NUMBER;
  CURSOR total_tables
  IS
     SELECT table_name FROM user_tables; 
BEGIN
  FOR i IN total_tables
  LOOP
SELECT COUNT (*) INTO table_count FROM user_tables WHERE db.table_name = i.table_name; --Wrong logic here
    DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
  END LOOP;
end proc_tab_count;

输出如下:

Table1 -COUNT:1
Table2 -COUNT:1
Table3 -COUNT:1
Table3 -COUNT:1
Table4 -COUNT:1
Table5 -COUNT:1

【问题讨论】:

  • 您无权访问dba_tables。您是否尝试过访问user_tables?或者要继续使用相同的代码,您将需要 DBA 权限。
  • 您在这里所做的只是遍历您的 USER_TABLES 并计算 USER_TABLES 中与当前表名匹配的行。它将始终为 1。您需要构建一些动态 SQL 来根据 USER_TABLES.TABLE_NAME 计算表中的行数。
  • 这也不起作用,因为在 PL/SQL 块中,您只有 直接 授予用户的权限。通过角色授予的权限不适用于 PL/SQL 块内,即过程或函数(使用 DEFINER RIGHTS 是默认设置)。所以,GRANT DBA TO MY_USER 不起作用,你必须使GRANT SELECT ANY DICTIONARY TO MY_USER

标签: oracle


【解决方案1】:

猜猜,您想计算所有表中的行数,我们需要一个动态 SQL 此处。 EXCEUTE IMMEDIATE 用于构建动态 SQL,使 OWNER.TABLE_NAME 动态化。

create or replace procedure proc_tab_count as 
  table_count NUMBER;
  CURSOR total_tables
  IS
     SELECT table_name FROM user_tables; 
BEGIN
  FOR i IN total_tables
  LOOP
     /* Handle Exceptions */
     BEGIN
       EXECUTE IMMEDIATE 'SELECT COUNT (*)  FROM '|| i.table_name  INTO table_count;
       DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
     EXCEPTION WHEN OTHERS 
     THEN
             DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
     END;
  END LOOP;
end proc_tab_count;

【讨论】:

  • 谢谢。我第一次来EXECUTE IMMEDIATE
【解决方案2】:

我不太确定接受的答案是否有效。 user_tables 是否包含 owner?否则,它就在现场。

无论如何。使用 SYS_REFCURSOR 的另一种方法:

CREATE OR REPLACE PROCEDURE proc_table_count      
  CURSOR total_tables IS
  SELECT table_name 
    FROM user_tables
ORDER BY table_name; 

  table_count NUMBER;
  v_sql       VARCHAR2(100);
  v_cursor    SYS_REFCURSOR;
BEGIN
  FOR i IN total_tables
  LOOP     
     BEGIN
       v_sql := 'SELECT COUNT (*) FROM '||i.table_name;
       OPEN v_cursor FOR v_sql;
       FETCH v_cursor INTO table_count;
       CLOSE v_cursor;
       DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
     EXCEPTION WHEN OTHERS 
     THEN
       DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
     END;
  END LOOP;
end proc_tab_count;

【讨论】:

  • 这个概念是EXECUTE IMMEDIATE,它非常适合单行返回查询。其中SYS_REFCURSOR 是动态打开CURSOR 的东西,我们不希望CURSOR 被打开和关闭这么多次。还要提一下,FOR LOOP 中的 CURSOR 已经隐式打开了一个 CURSOR。!我在我的答案here 中有一个理想的SYS_REFCURSOR 用法。最后是关于 USER_TABLES 中的所有者,你是对的。我使用dba_tables 进行了测试,因此它是!
  • 老实说,我不确定您的意思是什么,但在这种情况下,我自己可能会使用EXECUTE IMMEDIATE。只是想我会加入另一种解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多