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

Posted

技术标签:

【中文标题】创建一个新表,其中第一行作为旧表的列名【英文标题】:Create a new table with first rows as the column names from old Table 【发布时间】:2017-04-06 05:53:55 【问题描述】:

这个非常有趣的问题是创建存储过程来获取表名 oldTable 和新表名 newTable 的参数。 newTable 的列名应该是 oldTable 第一行的值,其余行是内容。 到目前为止,如果我知道以下查询中设计的列数,我就取得了成功。 这是从工具中导入数据所必需的,但没有选择标题行的选项,在此工具中列被命名为 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;

【问题讨论】:

不太清楚我是否理解。您想编写一个存储过程来基于“OLDTABLE”创建一个名为“NEWTABLE”的表,但不清楚它们之间的区别是什么。你能用CREATE TABLE NEWTABLE AS SELECT * FROM OLDTABLE WHERE 1=0 复制表的结构吗? 新表的列名是旧表的rowvalues,这是因为我们的导入系统没有选择标题行的选项。 我明白了吗?您有一个将文件内容插入通用表的导入系统。插入的第一行包含列名。我的感觉是创建视图可能比复制数据更好。然而,有(至少)两个问题。 1 - 插入的第一行不一定是查询检索到的第一行 - 是否有其他方法来标识列名的行。 2 - 你怎么知道每一列的数据类型? 您的第一个问题是确定哪一行包含列标题。是否插入了“行号”列? `它始终是插入顺序中的第一行,没有任何排序。`换句话说,您希望它是第一行,但您不能保证它是第一行,因为 OLD_TABLE 没有排序键或其他标识符。没有 ORDER BY 列的查询以未定义的顺序返回,尽管大多数时候你会很幸运。 【参考方案1】:

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

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;

【讨论】:

以上是关于创建一个新表,其中第一行作为旧表的列名的主要内容,如果未能解决你的问题,请参考以下文章

插入带有列的命令[重复]

ms sql一些技巧

MySQL拷贝表的几种方式

MySql中,复制旧表结构到新表

表的使用

怎么快速复制千万级的Mysql数据库表