创建一个新表,其中第一行作为旧表中的列名

时间:2017-04-06 05:53:56

标签: sql oracle plsql dynamic-sql

这个非常有趣的问题是创建存储过程来获取表名oldTable和新表名newTable的参数。 newTable的列名应为oldTable第一行的值,其余行为content。 如果我知道以下查询中设计的列数,那么我已经取得了成功。 这是从工具导入数据所必需的,但没有选择标题行的选项,此工具中的列被命名为field_1,field_2等。

--Following is a solution but does not work when number of columns vary :     
SELECT 'CREATE TABLE NEWTABLE AS SELECT ' || REGEXP_REPLACE(FIELD_0, '[^a-zA-Z'']') 
     || ',' || REGEXP_REPLACE(FIELD_1, '[^a-zA-Z'']') 
     || ',' ||REGEXP_REPLACE(FIELD_2, '[^a-zA-Z'']') 

     || ' FROM (SELECT ROWNUM  R,' 
           || 'FIELD_0 ' || REGEXP_REPLACE(FIELD_0, '[^a-zA-Z'']', '') 
           || ', FIELD_1 ' || REGEXP_REPLACE(FIELD_1, '[^a-zA-Z'']', '') 
           || ', FIELD_2 ' || REGEXP_REPLACE(FIELD_2, '[^a-zA-Z'']', '') 

           || ' FROM test) WHERE R <> 1'
      FROM oldTable
     WHERE ROWNUM = 1

这里oldTable的结构如下

 CREATE TABLE oldTable 
   (    "FIELD_0" VARCHAR2(30), 
    "FIELD_1" VARCHAR2(30), 
    "FIELD_2" VARCHAR2(30)
   )  ;
insert into oldTable (FIELD_0, FIELD_1, FIELD_2)
values ('a', 'b', 'c');

insert into oldTable (FIELD_0, FIELD_1, FIELD_2)
values ('apple', 'ball', 'cat');

insert into oldTable (FIELD_0, FIELD_1, FIELD_2)
values ('1', '23', '4');

Old Table
            FIELD_0 FIELD_1 FIELD_2
        1   a       b       c
        2   apple   ball    cat
        3   1       23      4

NewTable (First row of old table is Column of new table)
                a       b       c
            1   apple   ball    cat
            2   1       23      4

我正在尝试创建一个适用于任意列数的表的解决方案,如果有的话,该解决方案对许多人都有用。

如果我们可以像下面给出的那样遍历值和列,可能有解决方案。唯一的问题是获取第一行的值以构成动态查询。

BEGIN
    FOR col IN (select column_name
                  from cols
                 where upper(table_name) = upper('oldTable')) LOOP

     --Some code here
    END LOOP;
  END;

1 个答案:

答案 0 :(得分:3)

我过去遇到过这个问题,而我能够解决问题的唯一方法是使用DBMS_SQL。可以根据需要调整以下存储过程。有很多评论,仔细阅读,因为他们解释了这个程序是如何工作的。我假设您能够识别将成为新表中列名称的第一行。 :

PROCEDURE create_stg_tab(old_tab_name IN VARCHAR2, new_tab_name IN VARCHAR2)
IS
v_ct             number default 0;
v_col            varchar2(1000) default '';
l_theCursor      integer default dbms_sql.open_cursor; 
l_colCnt         number; 
l_descTbl        dbms_sql.desc_tab; 
l_columnValue    varchar2(4000); 
l_status         integer; 
v_dest_cols      varchar2(1000) default '';
v_sql            varchar2(1000) default '';
v_col_insert     varchar2(32000) default '';
v_sql_insert     varchar2(32000) default '';

BEGIN

--get the number of columns of the source table
select count(*) into v_ct from all_tab_cols where upper(table_name) = old_tab_name AND OWNER = <SCHEMA NAME>;

--build your dynamic source query
if v_ct > 0 then
    for i in (select column_name from all_tab_cols where table_name = old_tab_name AND OWNER = <SCHEMA NAME>)
    loop
        v_col := ltrim((v_col||','||i.column_name),',');
    end loop;

    --Get dynamic select all columns from old table
    v_sql := 'select '||v_col||' from '||old_tab_name;
    dbms_sql.parse(l_theCursor,v_sql,dbms_sql.native ); 
    dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl);

    for i in 1 .. l_colCnt loop 
        dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000); 
    end loop; 

    --execute cursor
    l_status := dbms_sql.execute(l_theCursor); 

    --loop through the rows 
    while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop 
        --loop through the columns
        for i in 1 .. l_colCnt loop 

            dbms_sql.column_value( l_theCursor, i, l_columnValue ); 
            IF l_columnValue IS NOT NULL THEN
                --REGEXP_REPLACE start from inner most (You can customize/ignore this): 
                --1)if value is just a number, replace it with the source column name
                --2)if value starts with anything except letters (e.g. start with number but has letters), remove the leading non-letters
                --3)replace all non leading characters (except numbers and letters) with '_'
                --Ex.1289789bbB#4B$5 => bbB_4B5, 222 => source_column name
                v_dest_cols := v_dest_cols || (l_descTbl(i).col_name||' as '||substr(REGEXP_REPLACE(REGEXP_replace(REGEXP_REPLACE(l_columnValue,'^\d+$', l_descTbl(i).col_name, 1),'^[^a-z,A-Z]+', null, 1),'[^a-z,A-Z,0-9]', '_'),0,30))||',';
                v_col_insert := ltrim((v_col_insert||','||l_descTbl(i).col_name),',');
            END IF;
        end loop; 

        --remove last ','
        v_dest_cols := rtrim(v_dest_cols,',');
        --build create new empty table statement
        v_sql := 'create table '||new_tab_name||' as select '||v_dest_cols||' from '||old_tab_name||' where 1=2';
        --build insert into the new table statement
        v_sql_insert := 'insert into '||new_tab_name||' select '||v_col_insert||' from '||old_tab_name||' WHERE <condition to fetch rows from 2nd onwards>';
    end loop; 

    --execute
    execute immediate v_sql;
    execute immediate v_sql_insert;
end if;

exception
    --your exception
END create_stg_tab;