使用统计变量立即执行sql

问题描述 投票:0回答:1

我想根据 Oracle 中每个表的主键元数据创建一些统计信息。我收到错误消息(底部)。我的脚本如下:

SET SERVEROUTPUT ON;
DECLARE
    v_owner            VARCHAR2(40);
    v_table_name       VARCHAR2(40);
    v_column_name      VARCHAR2(40);
    v_count_rows       NUMBER;
    v_count_real_rows  NUMBER;
    v_count_rows_diff  NUMBER;
    v_rn_tables        NUMBER;
    v_count_tables     NUMBER;
    v_max_primary_key  NUMBER;
    sql_stmt           VARCHAR2(32767);
    CURSOR get_tables IS
    SELECT
        cons.owner,
        cols.table_name,
        cols.column_name,
        nvl(num_rows, - 1)      AS count_rows,
        ROW_NUMBER()
        OVER(PARTITION BY cons.owner
             ORDER BY cols.table_name
        )                       AS rn_tables,
        COUNT(DISTINCT cols.table_name)
        OVER(PARTITION BY cons.owner
            -- ORDER BY cols.table_name
        )                       AS count_tables
    FROM
        all_constraints   cons,
        all_cons_columns  cols,
        all_tables        tab
    WHERE
            cols.table_name = tab.table_name
        AND cons.constraint_type = 'P'
        AND cons.constraint_name = cols.constraint_name
        AND cons.owner = cols.owner
        AND tab.table_name NOT LIKE '%_MV'
        AND cols.position = 1
    ORDER BY
        cons.owner,
        cols.table_name;

BEGIN
    OPEN get_tables;
    LOOP
        FETCH get_tables INTO
            v_owner,
            v_table_name,
            v_column_name,
            v_count_real_rows,
            v_rn_tables,
            v_count_tables;
        EXIT WHEN get_tables%notfound;
         -- dbms_output.put_line('Tabelle ' || v_owner ||' ' || v_table_name||' ' || v_column_name||' ' || v_count_real_rows  ); 

        sql_stmt := 'SELECT COUNT(*) as count_rows,max('||v_column_name||') as max_primary_key FROM '
                    || v_owner
                    || '.'
                    || v_table_name
                    ;
     --   dbms_output.put_line(sql_stmt);
        EXECUTE IMMEDIATE sql_stmt
        INTO v_count_rows,v_max_primary_key
        ;
            dbms_output.put_line(v_rn_tables ||' out of '||v_count_tables ||' '||v_owner
                                 || '.'
                                 || v_table_name
                                 || ': '
                                 || v_count_rows
                                 || ': '
                                 || v_count_real_rows
                                 || ': '
                                 || v_count_rows_diff);


    END LOOP;

    CLOSE get_tables;
END;

错误是:

    ORA-01722: invalid number
ORA-06512: at line 63
01722. 00000 -  "invalid number"
*Cause:    The specified number was invalid.
*Action:   Specify a valid number.

问题可能出在哪里?如果我在没有 v_max_primary_key 的情况下运行它,它就可以工作。

抱歉,我需要在这里输入一些文字,否则帖子将不被接受,请忽略这部分。

oracle plsql
1个回答
0
投票

您尝试选择主键最大值的列不是

NUMBER
列,因此 Oracle 会隐式尝试将值从任何数据类型转换为数字,但会失败。

当我在小提琴中运行查询时,输出为:

1 out of 3 CTXSYS.DR$OBJECT_ATTRIBUTE: 502: 502: 
2 out of 3 CTXSYS.DR$THS: 0: 0: 
3 out of 3 CTXSYS.DR$THS_PHRASE: 0: 0: 
1 out of 1 FIDDLE_KQOAUBPRJBOCOJMYPOKT.TEST: 1: -1: 
1 out of 35 MDSYS.NTV2_XML_DATA: 0: 0: 
2 out of 35 MDSYS.OGIS_SPATIAL_REFERENCE_SYSTEMS: 0: 0: 

ORA-01722: invalid number
ORA-06512: at line 60

接下来读取的表没有数字主键。


除此之外:

  • 您正在尝试查询数据库中所有用户的所有表;您可能希望将查询限制为特定用户的白名单。
  • 您加入了
    cons.owner = cols.owner
    ,但没有加入
    cols.owner = tab.owner

如果您创建示例数据:

CREATE TABLE test (id VARCHAR2(20) PRIMARY KEY);

INSERT INTO test(id) VALUES ('1');

然后(更新连接条件并将查询限制为当前用户):

DECLARE
    v_owner            VARCHAR2(40);
    v_table_name       VARCHAR2(40);
    v_column_name      VARCHAR2(40);
    v_count_rows       NUMBER;
    v_count_real_rows  NUMBER;
    v_count_rows_diff  NUMBER;
    v_rn_tables        NUMBER;
    v_count_tables     NUMBER;
    v_max_primary_key  NUMBER;
    sql_stmt           VARCHAR2(32767);
    CURSOR get_tables IS
    SELECT
        cons.owner,
        cols.table_name,
        cols.column_name,
        nvl(num_rows, - 1)      AS count_rows,
        ROW_NUMBER()
        OVER(PARTITION BY cons.owner
             ORDER BY cols.table_name
        )                       AS rn_tables,
        COUNT(DISTINCT cols.table_name)
        OVER(PARTITION BY cons.owner
            -- ORDER BY cols.table_name
        )                       AS count_tables
    FROM all_constraints   cons
         INNER JOIN all_cons_columns  cols
         ON (    cons.constraint_name = cols.constraint_name
             AND cons.owner = cols.owner)
         INNER JOIN all_tables        tab
         ON (    cols.table_name = tab.table_name
             AND cols.owner      = tab.owner)
    WHERE cons.constraint_type = 'P'
    AND   tab.table_name NOT LIKE '%_MV'
    AND   cols.position = 1
    AND   tab.owner = USER
    ORDER BY
        cons.owner,
        cols.table_name;

BEGIN
    OPEN get_tables;
    LOOP
        FETCH get_tables INTO
            v_owner,
            v_table_name,
            v_column_name,
            v_count_real_rows,
            v_rn_tables,
            v_count_tables;
        EXIT WHEN get_tables%notfound;
         -- dbms_output.put_line('Tabelle ' || v_owner ||' ' || v_table_name||' ' || v_column_name||' ' || v_count_real_rows  ); 

        sql_stmt := 'SELECT COUNT(*) as count_rows,max('||v_column_name||') as max_primary_key FROM '
                    || v_owner
                    || '.'
                    || v_table_name
                    ;
     --   dbms_output.put_line(sql_stmt);
        EXECUTE IMMEDIATE sql_stmt
        INTO v_count_rows,v_max_primary_key
        ;
            dbms_output.put_line(v_rn_tables ||' out of '||v_count_tables ||' '||v_owner
                                 || '.'
                                 || v_table_name
                                 || ': '
                                 || v_count_rows
                                 || ': '
                                 || v_count_real_rows
                                 || ': '
                                 || v_count_rows_diff);


    END LOOP;

    CLOSE get_tables;
END;
/

输出:

1 out of 1 FIDDLE_KQOAUBPRJBOCOJMYPOKT.TEST: 1: -1: 

如果添加了无法转换为数字的值:

INSERT INTO test(id) VALUES ('A');

那么输出是:

ORA-01722: invalid number
ORA-06512: at line 60

要么:

  • 将您的查询过滤为仅
    NUMBER
    列;或
  • 当数据不是数字时,不要尝试以
    NUMBER
    的形式获取值。尝试
    v_max_primary_key  VARCHAR2(4000);
    (尽管对于某些没有隐式转换为字符串的数据类型,这可能仍然会失败)。

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.