sql - Phantom LONG 数据类型导致我的 SQL 代码崩溃 - ORA-00997

标签 sql oracle plsql

我有以下代码,应该查找数据库中的每一列并输出列名、表名、数据类型、空值数和行数。

我遇到的问题是,当我运行它时,它运行大约两分钟,然后提示“非法使用 LONG 数据类型”,但我在这里没有使用任何 LONG 。

如果我编辑搜索以仅选择 WHERE rownum < 100(在以下代码中注释掉),则它可以正常工作。此外,如果我只执行 SELECT 语句,它运行得很好并输出所有正确的 SQL 语句。 (大约 18000 个)所以我猜测错误是在循环中的某个地方。

有关如何解决此问题的任何指导吗?

    SET SERVEROUTPUT ON;
    declare
       myCol1 varchar2(1000);
       myCol2 varchar2(1000);
       myCol3 varchar2(1000);
       myCol4 number;
       myCol5 number;
     begin
       for line in
       (
         SELECT
             'SELECT ''' || atc.column_name || ''', ''' || atc.table_name || ''', ''' || atc.data_type || ''', 
         SUM(CASE WHEN temp.'|| atc.column_name || ' IS NULL THEN 0 ELSE 1 END)           "Filled Values", 
             COUNT(temp.' || atc.column_name || ') "Total Records" 
             FROM all_tab_columns atc 
             JOIN '|| atc.table_name || ' temp ON atc.column_name = ''' || 
             atc.column_name ||''' AND atc.table_name = ''' || atc.table_name || '''' AS SQLRow 
         FROM all_tab_columns atc --WHERE rownum < 100
       )
       loop
          execute immediate line.Sqlrow into myCol1, myCol2, myCol3, myCol4, myCol5;
          INSERT INTO results VALUES (myCol1, myCol2, myCol3, myCol4, myCol5);
       end loop;


     end;
     SELECT * FROM results;

     /

最佳答案

正在选取的其中一个表有一个LONG 列。您的静态代码并不直接引用它,但您生成的动态 SQL 是,例如

SELECT 'SQL_TEXT', 'OL$', 'LONG', 
     SUM(CASE WHEN temp.SQL_TEXT IS NULL THEN 0 ELSE 1 END) "Filled Values", 
         COUNT(temp.SQL_TEXT) "Total Records" 
         FROM all_tab_columns atc 
         JOIN OL$ temp ON atc.column_name = 'SQL_TEXT' AND atc.table_name = 'OL$'

它提示 COUNT。您无法将聚合(即使是看似简单的计数)应用于 LONG 列。或任何内置函数;来自the data types documentation :

In addition, LONG columns cannot appear in these parts of SQL statements:

  • GROUP BY clauses, ORDER BY clauses, or CONNECT BY clauses or with the DISTINCT operator in SELECT statements

  • The UNIQUE operator of a SELECT statement

  • The column list of a CREATE CLUSTER statement

  • The CLUSTER clause of a CREATE MATERIALIZED VIEW statement

  • SQL built-in functions, expressions, or conditions

...

ROWNUM 过滤器恰好在遇到数据字典中的任何 LONG 列之前停止。

要对其他所有内容运行此操作,您需要从查询中排除 LONG 列。不过,您可能想限制它您选择的模式;报告系统表/列的数据类型似乎有点奇怪。

我不确定您为什么要在生成的查询中加入回 all_tab_columns。这将得到相同的结果(对于同一表中具有不同数据类型的列):

SELECT 'SPARE2', 'OL$', 'VARCHAR2', 
     SUM(CASE WHEN temp."SPARE2" IS NULL THEN 0 ELSE 1 END), 
     COUNT(temp."SPARE2")
     FROM SYSTEM."OL$" temp 

COUNT 仅计算非空值,因此它会给出与 SUM 相同的结果(除非表为空时总和给出 null)。如果要计算所有行数,则计算一个常量,而不是列名。所以你可以这样做:

SELECT 'SPARE2', 'OL$', 'VARCHAR2', 
     COUNT(temp."SPARE2"),
     COUNT(1)
     FROM SYSTEM."OL$" temp 

通过根据数据类型更改动态查询,您可以为 LONG 和 LOB 值提供 null 结果,而不是完全跳过这些列。您可能还想引用所有标识符,以防出现大小写混合或其他问题:

   for line in (
     SELECT
         'SELECT ''' || atc.column_name || ''', '
           || '''' || atc.table_name || ''', '
           || '''' || atc.data_type || ''', '
           || CASE WHEN DATA_TYPE IN ('LONG', 'CLOB', 'BLOB') THEN 'NULL'
             ELSE 'COUNT(temp."' || atc.column_name || '")' END || ', '
           || 'COUNT(1) '
         || 'FROM '|| atc.owner || '."' || atc.table_name || '" temp ' AS SQLRow 
    FROM all_tab_columns atc 
    WHERE owner NOT IN ('SYS', 'SYSTEM') -- and others
   ) loop

或者,如果您也想获得这些的非空计数,我想也可以使用您的 SUM 版本,但使用 NVL ,因此它会报告空表为零。

关于sql - Phantom LONG 数据类型导致我的 SQL 代码崩溃 - ORA-00997,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24615385/

相关文章:

SQL递归方法添加自增

oracle - 使用子查询创建 View ,并为 ORACLE 9i 中的列添加别名!

sql - where 子句中的聚合函数

database - 获取oracle错误ORA-12541

oracle - 我可以使用 Oracle PL/SQL 同时持有两个锁吗?

sql - 正则表达式获取除范围内的所有数字

oracle sql : collect aggregation

mysql - 如何重新排列 ID 订单?

sql - 具有多个 SET 的 PostgreSQL UPDATE

android - SQLite ORDER BY 'yyyy-mm-dd' 格式的月份号