2011-10-19 108 views
2

我已经通过搜索,发现只有这个问题: Loop through columns SQL 它在某些方面类似,但不涉及PL/SQL和Oracle数据库,因此我要求新的问题。如何通过PL/SQL循环遍历列

我有一张桌子与约。 2000行和600列。有一些列只包含每行中的NULL。我想要做的是编写一个PL/SQL过程来从表中删除这些列。 所以我遇到了一个问题,我想通过all_tab_columns视图的帮助来遍历PL/SQL中的列。您可以在下面看到我的代码(我的表名是PreparedDocumentFeaturesValues):

PROCEDURE dropNullColumns AS 
    l_query VARCHAR2(10000); 
    all_row_count NUMBER; 
    null_row_count NUMBER; 
BEGIN 
    SELECT count(*) 
    INTO all_row_count 
    FROM PreparedDocumentFeaturesValues; 

    FOR columnItem IN (SELECT column_name 
         FROM all_tab_columns 
         WHERE TABLE_NAME = UPPER('PreparedDocumentFeaturesValues')) 
    LOOP 
     SELECT count(*) 
     INTO null_row_count 
     FROM PreparedDocumentFeaturesValues 
     WHERE columnItem.column_name IS NULL; 

     IF all_row_count=null_row_count THEN 
     l_query := 'ALTER TABLE PreparedDocumentFeaturesValues DROP COLUMN ' || columnItem.column_name; 
     EXECUTE IMMEDIATE l_query; 
     END IF; 
    END LOOP; 
END; 

的问题是,声明:

SELECT count(*) 
INTO null_row_count 
FROM PreparedDocumentFeaturesValues 
WHERE columnItem.column_name IS NULL; 

具有字符类型的列名和null_row_count总是等于0

我很确定,这里有人知道我该如何处理这个问题(通过改进上面的代码,或者有没有其他方法可以做这样的事情?> 在此先感谢您的帮助。

回答

2

由于在编译时您不知道列名,所以您的查询也需要使用动态SQL。像

PROCEDURE dropNullColumns AS 
    l_query VARCHAR2(10000); 
    all_row_count NUMBER; 
    null_row_count NUMBER; 
BEGIN 
    SELECT count(*) 
    INTO all_row_count 
    FROM PreparedDocumentFeaturesValues; 
    FOR columnItem IN (SELECT column_name 
         FROM all_tab_columns 
         WHERE TABLE_NAME = UPPER('PreparedDocumentFeaturesValues')) 
    LOOP 
     l_query := 'SELECT COUNT(*) ' || 
       ' FROM PreparedDocumentFeaturesValues ' || 
       ' WHERE ' || columnItem.column_name || ' IS NULL'; 
     EXECUTE IMMEDIATE l_query 
     INTO null_row_count; 

     IF all_row_count=null_row_count THEN 
     l_query := 'ALTER TABLE PreparedDocumentFeaturesValues DROP COLUMN ' || columnItem.column_name; 
     EXECUTE IMMEDIATE l_query; 
     END IF; 
    END LOOP; 
END; 

东西,你很可能也只是算一个非空行

PROCEDURE dropNullColumns AS 
    l_query VARCHAR2(10000); 
    not_null_row_count NUMBER; 
BEGIN 
    FOR columnItem IN (SELECT column_name 
         FROM all_tab_columns 
         WHERE TABLE_NAME = UPPER('PreparedDocumentFeaturesValues')) 
    LOOP 
     l_query := 'SELECT 1 from (SELECT COUNT(*) ' || 
       ' FROM PreparedDocumentFeaturesValues ' || 
       ' WHERE ' || columnItem.column_name || ' IS NOT NULL ' || 
       ' ) WHERE rownum < 2'; 
     EXECUTE IMMEDIATE l_query 
     INTO not_null_row_count; 

     IF not_null_row_count=0 THEN 
     l_query := 'ALTER TABLE PreparedDocumentFeaturesValues DROP COLUMN ' || columnItem.column_name; 
     EXECUTE IMMEDIATE l_query; 
     END IF; 
    END LOOP; 
END; 

简化逻辑有点这还有一个好处是,如果你碰巧有任何列的任何索引,循环中的查询可能会使用这些查询。查询可以一找到一个非空值而不是扫描整个表就停下来。

2

我相信你想

execute immediate 'SELECT count(*) FROM PreparedDocumentFeaturesValues WHERE '|| columnItem.column_name||' IS NULL' into null_row_count; 

下面是一个更完整的答案,这将是比你所拥有的上述更好的性能。

DVLP SQL>create table foo as select * from dba_objects where rownum < 10; 

Table created. 

DVLP SQL>update foo set status = null; 

9 rows updated. 

DVLP SQL> 
DVLP SQL>declare 
    2 tab_name constant varchar2(32) := 'foo'; 
    3 not_null number; 
    4 begin 
    5  for x in (select column_name from all_tab_columns where table_name = upper(tab_name)) loop 
    6  dbms_output.put('Checking '||tab_name||'.'||x.column_name); 
    7  begin 
    8   execute immediate 'select 1 from (select 1 from '||tab_name|| 
    9   ' where '||x.column_name||' is not null) where rownum = 1' into not_null; 
10   dbms_output.put_line('.'); 
11  exception when NO_DATA_FOUND then 
12   dbms_output.put_line('...all null.'); 
13  end; 
14  end loop; 
15 end; 
16/
Checking foo.OWNER. 
Checking foo.OBJECT_NAME. 
Checking foo.SUBOBJECT_NAME...all null. 
Checking foo.OBJECT_ID. 
Checking foo.DATA_OBJECT_ID. 
Checking foo.OBJECT_TYPE. 
Checking foo.CREATED. 
Checking foo.LAST_DDL_TIME. 
Checking foo.TIMESTAMP. 
Checking foo.STATUS...all null. 
Checking foo.TEMPORARY. 
Checking foo.GENERATED. 
Checking foo.SECONDARY. 
Checking foo.NAMESPACE. 
Checking foo.EDITION_NAME...all null.