Phantom LONG数据类型正在崩溃我的SQL代码 - ORA-00997

时间:2014-07-07 16:28:17

标签: 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;

     /

1 个答案:

答案 0 :(得分:3)

其中一个表格有一个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

  

此外,LONG列不能出现在SQL的这些部分中   语句:

     
      
  • GROUP BY子句,ORDER BY子句或CONNECT BY子句或SELECT语句中的DISTINCT运算符

  •   
  • SELECT语句的UNIQUE运算符

  •   
  • CREATE CLUSTER语句的列列表

  •   
  • CREATE MATERIALIZED VIEW语句的CLUSTER子句

  •   
  • SQL内置函数,表达式或条件

  •   
     

...

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所以它会为空表报告零。