使用 DB Link 从远程表中获取数据的 PL/SQL 过程

PL/SQL procedure to fetch data from remote tables using DB Link

提问人:Asha Ganesan 提问时间:8/30/2023 最后编辑:Asha Ganesan 更新时间:8/31/2023 访问量:61

问:

我是PL / SQL编程的新手。

我的要求是收集特定架构下的表列表、其计数 (*) 和num_rows以及 n 个数据库last_analyzed日期并将其填充到单个表中。我正在尝试通过数据库链接访问远程数据库。在互联网资源的帮助下设法编写了以下代码。但无法获得输出。请帮忙。.

CREATE OR REPLACE PROCEDURE fetch_tables_stats
AS
BEGIN
  FOR src_link IN (SELECT db_link FROM dba_db_links WHERE USERNAME = '<user>') 
  LOOP
    FOR tbl IN (SELECT table_name FROM dba_tables@src_link.db_link WHERE owner = '<owner>')
    LOOP
      DECLARE
      v_row_count NUMBER;
      v_num_rows NUMBER;
      sql_stmt1 VARCHAR2(1000);
      sql_stmt2 VARCHAR2(1000);
      BEGIN
        sql_stmt1 := 'SELECT COUNT(*) FROM ' || tbl || '.' || table_name || '@' ||src_link || '.' || db_link;
        EXECUTE IMMEDIATE sql_stmt1 INTO v_row_count;
        sql_stmt2 := 'SELECT num_rows FROM dba_tab_statistics@' || src_link || '.' || db_link || ' WHERE table_name = ' || tbl ||'.' || table_name || ' AND owner = ' || q'['<owner>']';  
        EXECUTE IMMEDIATE sql_stmt2 INTO v_num_rows;
        INSERT INTO SWMS_TAB_COUNT_STATS_RDS (OPCO_NAME,TABLE_NAME,ACTUAL_ROWCOUNT,STATS_NUM_ROWS,STATS_DATE)
        VALUES (src_link.db_link,tbl.table_name, v_row_count, v_num_rows,sysdate);
        COMMIT;
      END;
    END LOOP;
  END LOOP; 
END fetch_tables_stats;
/ 

输出:

LINE/COL ERROR
-------- -----------------------------------------------------------------
6/17     PL/SQL: ORA-00942: table or view does not exist
6/17     PL/SQL: SQL Statement ignored
14/6     PL/SQL: Statement ignored
14/46    PLS-00364: loop index variable 'TBL' use is invalid
16/3     PL/SQL: Statement ignored
16/16    PLS-00306: wrong number or types of arguments in call to '||'
18/6     PL/SQL: SQL Statement ignored
19/34    PLS-00364: loop index variable 'TBL' use is invalid
19/38    PL/SQL: ORA-00984: column not allowed here
SQL>

现在可以创建程序..但它不会填充表中的数据。.

SQL> create or replace procedure fetch_tables_stats
as
begin
  declare
    rc sys_refcursor;
    l_table_name dba_tables.table_name%type;
    v_row_count number;
        v_num_rows NUMBER;
        v_last_analyzed NUMBER;
        sql_stmt1 VARCHAR2(1000);
        sql_stmt2 VARCHAR2(1000);
        sql_stmt3 VARCHAR2(1000);
    begin
      for src_link in (select db_link from user_db_links)
      loop
        open rc for 'select table_name from dba_tables@' || src_link.db_link || 'where owner = ' || q'['SWMS']';
        loop
          fetch rc into l_table_name;
          exit when rc%notfound;
          sql_stmt1 := 'select count(*) from ' || l_table_name ||'@'|| src_link.db_link;
                  execute immediate sql_stmt1 into v_row_count;
                  sql_stmt2 := 'SELECT num_rows,last_analyzed FROM dba_tables@' || src_link.db_link || ' WHERE table_name = ' || l_table_name || ' AND owner = ' || q'['SWMS']';
                  execute immediate sql_stmt2 into v_num_rows, v_last_analyzed;
                  sql_stmt3 := 'INSERT INTO SWMS_TAB_COUNT_STATS_RDS (OPCO_NAME,TABLE_NAME,ACTUAL_ROWCOUNT,STATS_NUM_ROWS,STATS_DATE)
                  VALUES (' || src_link.db_link || ',' || l_table_name || ', v_row_count, v_num_rows,v_last_analyzed)';
                  execute immediate sql_stmt3;
                  commit;
        end loop;
        close rc;
      end loop;
    end;
end fetch_tables_stats;
/  2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   33

Procedure created.


SQL> exec fetch_tables_stats;

PL/SQL procedure successfully completed.

SQL> select count(*) from SWMS_TAB_COUNT_STATS_RDS;

  COUNT(*)
----------
         0
Oracle 循环 PLSQL 嵌套 DBLink

评论

0赞 Paul W 8/30/2023
你真的需要数一数吗?为什么不直接使用统计数据并称其为好呢?我们不应该对甲骨文的统计数据收集进行事后猜测。另请注意,您可以从查询中获取多行(每个分区有一行,每个表可能有多行,并且还可以将其他类型的统计信息表示为附加行,例如实时/DML 统计信息),因此您可能会在那里出错。我建议改用。num_rowsdba_tab_statisticsdba_tables
0赞 Asha Ganesan 8/31/2023
谢谢你的建议保罗..是的,当然,我同意使用dba_tables会更合适。而且,关于 count(*) 和 num_rows,我们的环境中有许多高度不稳定的表。收集这些详细信息以锁定这些详细信息的统计信息。
0赞 Paul W 8/31/2023
Oracle 跟踪 DML 更改(请参阅 ),并自动精确地设置段过期以处理易失性表。只要您有一个正确配置的统计信息收集制度,就不必为了启动统计信息收集而将实际计数与统计信息计数进行比较。使用过期功能。为了跟踪行计数随时间的变化,即使没有定期收集统计信息,将收集的统计信息添加到中也足够准确。dba_tab_modifications(inserts-deletes)dba_tab_modificationsnum_rowsdba_tables
0赞 Asha Ganesan 9/1/2023
谢谢!我会将过时列放入表中。

答:

1赞 Littlefoot 8/30/2023 #1

由于您必须在查询中包含表名和数据库链接名,因此您需要动态 SQL

下面是一个示例;我正在使用表格,因为我无权访问表格,所以 - 你必须解决这个问题。另外,我只是将输出显示到屏幕上 - 您将将其插入到表格中(也修复它)。user_...dba_...

SQL> set serveroutput on
SQL> declare
  2    rc           sys_refcursor;
  3    l_table_name user_tables.table_name%type;
  4    l_cnt        number;
  5  begin
  6    for src_link in (select db_link from user_db_links) loop
  7
  8      open rc for 'select table_name from user_tables@' || src_link.db_link;
  9
 10      loop
 11        fetch rc into l_table_name;
 12        exit when rc%notfound;
 13
 14        execute immediate 'select count(*) from ' || l_table_name ||'@'|| src_link.db_link
 15          into l_cnt;
 16
 17        dbms_output.put_line(l_table_name||'@'|| src_link.db_link ||' -> ' || l_cnt || ' row(s)');
 18      end loop;
 19
 20      close rc;
 21    end loop;
 22  end;
 23  /
BONUS@DBL_MIKE -> 0 row(s)
DUMMY@DBL_MIKE -> 1 row(s)
SALGRADE@DBL_MIKE -> 5 row(s)
DEMO_TAGS@DBL_MIKE -> 6 row(s)
<snip>

PL/SQL procedure successfully completed.

SQL>

[编辑]

例如,您的过程存在一些错误

  • 缺少前面的空间WHERERC
  • 缺少将表名括起来的单引号SQL_STMT2
  • 变量的数据类型错误(应该是 ,而不是V_LAST_ANALYZEDDATENUMBER)
  • 良好的实践表明,在使用动态 SQL 时,您应该首先显示要执行的语句 (using ),然后,当您确定它看起来没问题时,实际使用 .没有它,你就无法真正确定你要执行什么dbms_output.put_lineexecute immediate
  • 您不需要动态 SQL 将行插入到目标表中;它没有任何动态
  • 良好的做法是,您应该避免在循环中提交

修复后,它可以工作

示例目标表:

SQL> CREATE TABLE swms_tab_count_stats_rds
  2  (
  3     opco_name         VARCHAR2 (30),
  4     table_name        VARCHAR2 (30),
  5     actual_rowcount   NUMBER,
  6     stats_num_rows    NUMBER,
  7     stats_date        DATE
  8  );

Table created.

过程(我使用和我自己的用户,):all_tablesmike

SQL> CREATE OR REPLACE PROCEDURE fetch_tables_stats
  2  AS
  3  BEGIN
  4     DECLARE
  5        rc               SYS_REFCURSOR;
  6        l_table_name     all_tables.table_name%TYPE;
  7        v_row_count      NUMBER;
  8        v_num_rows       NUMBER;
  9        v_last_analyzed  DATE;
 10        sql_stmt1        VARCHAR2 (1000);
 11        sql_stmt2        VARCHAR2 (1000);
 12     BEGIN
 13        DELETE FROM swms_tab_count_stats_rds;
 14
 15        FOR src_link IN (SELECT db_link FROM all_db_links)
 16        LOOP
 17           OPEN rc FOR
 18                 'select table_name from all_tables@'
 19              || src_link.db_link
 20              || ' where owner = '
 21              || q'['MIKE']';
 22
 23           LOOP
 24              FETCH rc INTO l_table_name;
 25
 26              EXIT WHEN rc%NOTFOUND;
 27              sql_stmt1 :=
 28                 'select count(*) from ' || l_table_name || '@' || src_link.db_link;
 29
 30              EXECUTE IMMEDIATE sql_stmt1
 31                 INTO v_row_count;
 32
 33              sql_stmt2 :=
 34                    'SELECT num_rows, last_analyzed FROM all_tables@'
 35                 || src_link.db_link
 36                 || ' WHERE table_name = '
 37                 || CHR (39)
 38                 || l_table_name
 39                 || CHR (39)
 40                 || ' AND owner = '
 41                 || q'['MIKE']';
 42
 43              EXECUTE IMMEDIATE sql_stmt2
 44                 INTO v_num_rows, v_last_analyzed;
 45
 46              INSERT INTO swms_tab_count_stats_rds (opco_name,
 47                                                    table_name,
 48                                                    actual_rowcount,
 49                                                    stats_num_rows,
 50                                                    stats_date)
 51                   VALUES (src_link.db_link,
 52                           l_table_name,
 53                           v_row_count,
 54                           v_num_rows,
 55                           v_last_analyzed);
 56           END LOOP;
 57
 58           CLOSE rc;
 59        END LOOP;
 60
 61        COMMIT;
 62     END;
 63  END fetch_tables_stats;
 64  /

Procedure created.

测试:

SQL> EXEC fetch_tables_stats;

PL/SQL procedure successfully completed.

SQL>   SELECT *
  2      FROM swms_tab_count_stats_rds
  3  ORDER BY stats_date;
    
OPCO_NAME  TABLE_NAME           ACTUAL_ROWCOUNT STATS_NUM_ROWS STATS_DATE
---------- -------------------- --------------- -------------- -------------------
DBL_MIKE   BONUS                              0              0 17.05.2018 08:57:22
DBL_MIKE   SALGRADE                           5              5 17.05.2018 22:00:10
DBL_MIKE   DUMMY                              1              1 17.05.2018 22:00:10
DBL_MIKE   DEMO_ORDERS                       10             10 06.10.2018 22:01:03
DBL_MIKE   APEX$TEAM_DEV_FILES                0              0 06.10.2018 22:01:03
<snip>

评论

0赞 Asha Ganesan 8/31/2023
谢谢你的建议!我能够创建如下程序..但数据未填充到表中。.
0赞 Littlefoot 8/31/2023
为什么不呢?发生了什么事?
0赞 Asha Ganesan 8/31/2023
我附加了修改后的代码..你能审查一下吗?
0赞 Littlefoot 8/31/2023
是的;我审查了它并编辑了我的答案,以显示您应该做什么。请看一看。
0赞 Asha Ganesan 9/1/2023
哇!你匿名救了我的背!非常感谢您教我这个更正:-)上帝保佑你!