遍历行的列

时间:2017-07-21 12:35:37

标签: sql oracle

我有100列没有相关名称的表(ABC1,DA23,EE123 - 那里没有共同的模式)。

我想遍历此表中的每一行和每一列。

我目前的剧本:

BEGIN
   FOR single_row IN (
      SELECT *
      FROM MY_TABLE) 
   LOOP
      --iterate through columns of 'single_row'
      --for each nullable column do insert with real current column name and column value)
      --I assume each column is nullable except of ID
      INSERT INTO ANOTHER_TABLE VALUES (single_row.id, column_name, column_value);
   END LOOP;
END;

例如,如果MY_TABLE包含2行:

ID|ABC1|DA23|EE123|...
1|123|456|789|...
2|321|654|987|...

运行我的脚本后,我的ANOTHER_TABLE将包含:

MY_TABLE_ID|COLUMN_NAME|COLUMN_VALUE
1|ABC1|123
1|DA23|456
1|EE123|789
... other columns from row 1
2|ABC1|321
2|DA23|654
2|EE123|987
... other columns from row 2

我怎么能这样做?

我正在使用Oracle 11g

修改

@vkp提供了很好的解决方案,但还有一件事需要解决。我不想在in子句中指定所有列。我希望在那里使用某种查询或*或其他任何东西,只是为了不被强制列出所有这些。

我尝试过这样的事情:

select * 
from MY_TABLE t
unpivot (
   column_value for column_name in (select column_name 
        from user_tab_columns
        where table_name = 'MY_TABLE'
        and nullable = 'Y')
) u

但它返回错误:

ORA-00904: : invalid identifier
00904. 00000 -  "%s: invalid identifier"

3 个答案:

答案 0 :(得分:2)

这是unpivot

的应用程序
select *
from my_table m
unpivot (column_value for column_name in (ABC1,DA23,EE123)) u
任何ID列的

null值都不会显示在结果中。

如果必须在输出中包含空值,请使用选项INCLUDE NULLS

select *
from my_table m
unpivot include nulls (column_value for column_name in (ABC1,DA23,EE123)) u

编辑:要动态包含列名,请使用

DECLARE 
sql_stmt VARCHAR2(4000);
var_columns VARCHAR2(4000); --use clob datatype if the column names can't fit in with this datatype

BEGIN
SELECT LISTAGG(column_name,',') WITHIN GROUP(ORDER BY column_name) 
INTO var_columns 
FROM user_tab_columns
WHERE table_name='MY_TABLE' AND column_name<>'ID';

sql_stmt:='select * from my_table m 
           unpivot 
           (column_value for column_name in (' || var_columns || ')) u';

EXECUTE IMMEDIATE sql_stmt;
END;
/

答案 1 :(得分:0)

第一个选项。用动态sql。

declare
         v_ctx  number;
         v_query  varchar2(500);
         v_total  NUMBER;         
         v_desctab    DBMS_SQL.DESC_TAB;
         v_column_cnt     NUMBER;      
         v_value   varchar2(32767);
         v_result clob := '';
         v_rownum number := 0;
    begin
        v_ctx := dbms_sql.open_cursor;
        v_query := 'select * from user_objects where rownum < 100';
        dbms_sql.parse(v_ctx,v_query,dbms_sql.v7);     
        v_total := dbms_sql.execute(v_ctx);  
        DBMS_SQL.DESCRIBE_COLUMNS(v_ctx, v_column_cnt, v_desctab);   
        for i in 1 .. v_column_cnt loop
          dbms_sql.define_column(v_ctx, i, v_value /* data_type varchar2*/, 32767 /* max_length*/);
        end loop;                   
   loop
        exit when dbms_sql.fetch_rows(v_ctx) = 0;
        v_rownum := v_rownum +1;
         for i in 1 .. v_column_cnt loop           
           dbms_sql.column_value(v_ctx, i, v_value);
           dbms_output.put_line(v_rownum||' - '||v_desctab(i).col_name||' - '||v_value);
         end loop;         
   end loop;
   dbms_sql.close_cursor(v_ctx);
   exception
        when others then
                dbms_sql.close_cursor(v_ctx);
                  raise;

   end;
   /

xquery的第二个选项。

select t1.id,t2.* from xmltable('for $i in ora:view("<you_table_here>")/ROW
           return $i'
           columns id FOR ORDINALITY
           , row_value xmltype path'.'          
            ) t1
            ,xmltable('for $i in $row_value/ROW/* return $i' 
              passing t1.row_value as "row_value" 
               columns col_index for ORDINALITY ,
                  column_name varchar2(100) path 'name()',
                  column_value   varchar2(100) path 'text()'
             )  t2

答案 2 :(得分:0)

这是一个使用REF CURSOR的简单解决方案。

我已经尝试过这段代码,而且它已经在我的最后工作了。

DECLARE
 query_2 VARCHAR2(1000);
 TYPE icur IS REF CURSOR;
 ic icur;
 col_val VARCHAR2(100);
 BEGIN
     FOR j IN
     (SELECT * FROM user_tab_cols WHERE table_name = UPPER('MY_TABLE'))
 LOOP
     dbms_output.put_line(j.column_name);
     query_2 := 'SELECT ' || j.column_name|| '  FROM MY_TABLE';
 OPEN ic FOR query_2;
 LOOP
     FETCH ic INTO col_val;
     EXIT WHEN ic%NOTFOUND;
     INSERT INTO ANOTHER_TABLE VALUES( j.column_name, col_val);
 END LOOP;

END LOOP; END; /