2011-10-19 29 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.