【问题标题】:CREATE AS SELECT * but with one column obtained from another tableCREATE AS SELECT * 但从另一个表中获得一列
【发布时间】:2018-06-27 12:15:55
【问题描述】:

我需要使用CREATE AS SELECT 语句“重新创建”超过 50 个表(在 Oracle 中)。但是,所有这些表都将使用另一表中的数据修改一列。有没有一种方法可以在不声明 SELECT 语句中的每一列的情况下实现这一点?

类似:

CREATE TABLE table_name_copy AS SELECT *, (SELECT col_name FROM other_table WHERE other_table.col_id = table_name.col_id) AS col_name FROM table_name`

基本上在所有表上,我都有一个需要用other_table 列中的数据替换的列。

【问题讨论】:

    标签: sql oracle plsql


    【解决方案1】:

    这样生成 SQL 字符串:

    SELECT 'CREATE TABLE table_name_copy AS SELECT '
        || LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_name)
        || ', (SELECT col_name FROM other_table 
               WHERE other_table.col_id = table_name.col_id) AS col_name'
        || ' FROM table_name'
    FROM all_tab_cols
    WHERE owner = 'OWNER'
    AND table_name = 'TABLE_NAME'
    AND column_name != 'COL_NAME'
    

    如果你想运行上面的语句,你可以使用EXECUTE IMMEDIATE:

    DECLARE
      v_sql VARCHAR2(10000);
    BEGIN
      SELECT 'CREATE TABLE table_name_copy AS SELECT '
          || LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_name)
          || ', (SELECT col_name FROM other_table 
                 WHERE other_table.col_id = table_name.col_id) AS col_name'
          || ' FROM table_name'
      INTO v_sql
      FROM all_tab_cols
      WHERE owner = 'OWNER'
      AND table_name = 'TABLE_NAME'
      AND column_name != 'COL_NAME';
    
      EXECUTE IMMEDIATE v_sql;
    END;
    /
    

    【讨论】:

    • 我尝试运行它,但我得到了:Error report - ORA-00936: missing expression. ORA-06512: at line 14. 00936. 00000 - "missing expression"...
    • @FedeE。好吧,您将不得不用架构中的实际标识符替换 OWNERTABLE_NAMECOL_NAME... ;-)
    • 我做到了:)。但我仍然得到那个缺失的表达。我尝试像这样单独运行 create as select:CREATE TABLE ORIG_TABLE_COPY AS SELECT LISTAGG (column_name_to_replace, ', ') WITHIN GROUP (ORDER BY column_name_to_replace), (SELECT new_data_for_column FROM OTHER_TABLE_WITH_NEW_DATA WHERE OTHER_TABLE_WITH_NEW_DATA.id_column = ORIG_TABLE.id_column) AS column_name_to_replace FROM ORIG_TABLE; 抛出:ORA-00998: must name this expression with a column alias. 00998. 00000 - "must name this expression with a column alias"
    • 嗯,你为什么要那样做?你明白我的例子想要做什么吗?它是一个 SQL 语句,生成您要执行的 SQL 语句。
    • 只是想找出它为什么不起作用:)
    【解决方案2】:

    如果col_id 列对于两个连接的表都是固定的,

    您可以通过模式使用user_tab_columnsuser_tables 字典视图通过以下机制生成名为“table_name_copy”的新表:

    declare
     v_ddl varchar2(4000); 
     v_cln varchar2(400); 
    begin
     for c in ( select * 
                  from user_tables t 
                 where t.table_name in
                 ( select c.table_name 
                     from user_tab_columns c
                    where c.column_name = 'COL_ID'  ) 
                 order by t.table_name )
     loop    
       v_ddl := 'create table '||c.table_name||'_copy as 
                          select ';
       for d in ( select listagg('t1.'||column_name, ',') within group ( order by column_name ) cln 
                    from user_tab_columns 
                   where table_name = c.table_name 
                     and column_name != 'COL_ID' )                   
       loop
        v_cln := v_cln||d.cln;  
       end loop;
           v_ddl := v_ddl||v_cln;                       
           v_ddl := v_ddl||', t2.col_id t2_id 
            from '||c.table_name||' t1 
            left outer join other_table t2 on ( t1.col_id = t2.col_id )';
    
           execute immediate v_ddl;                    
           v_ddl := null;
           v_cln := null;
     end loop;
    end;
    

    【讨论】:

    • 这给出了Error report - ORA-06550: line 20, column 26: PLS-00103: Encountered the symbol ")" when expecting one of the following: . ( * @ % & = - + ; < / > at in is mod remainder not rem <an exponent (**)> <> or != or ~= >= <= <> and or like like2 like4 likec between || member submultiset The symbol "(" was substituted for ")" to continue. 06550. 00000 - "line %s, column %s:\n%s" *Cause: Usually a PL/SQL compilation error. *Action: 错误。
    • @FedeE。对不起,有两个被遗忘的小错字,现在已修复。
    • Arrgh:现在我得到Error report - ORA-00933: SQL command not properly ended ORA-06512: at line 27 00933. 00000 - "SQL command not properly ended"hehe
    • @FedeE。刚才我已经测试和编辑了,请再看看。
    • Oracle 快把我逼疯了……我以为它在运行,但后来我得到:Error report - ORA-00972: identifier is too long ORA-06512: at line 27 00972. 00000 - "identifier is too long" *Cause: An identifier with more than 30 characters was specified. *Action: Specify at most 30 characters.
    【解决方案3】:

    也许您可以使用简单的连接和星号来返回第一个表中的所有列,如下所示:

    CREATE TABLE table_name_copy AS 
      SELECT * FROM (
        SELECT tab1.*, tab2.column_name 
        FROM table_name tab1 LEFT JOIN other_table tab2 ON tab1.col_id = tab2.col_id
      );
    

    【讨论】:

    • 不会“重新格式化”表格列的顺序吗?我的意思是,我最终将拥有原始表中的所有列以及新表末尾的新列。
    • 嗯,我明白了,您需要替换列,而不是创建新列。在这种情况下,你是对的,它不会按预期工作。
    • 感谢您的尝试!
    【解决方案4】:

    我会试试这个(但我没有要测试的 Oracle SQL,所以请不要怀疑)

    CREATE TABLE table_name_copy AS 
    
    SELECT * FROM (
                   SELECT *, (SELECT col_name FROM other_table WHERE other_table.col_id = table_name.col_id) as col_name 
    
                   FROM table_name`
                   )
    

    编辑: 然后运行

    ALTER TABLE table_name_copy DROP COLUMN <old column>
    删除不再需要的列

    【讨论】:

    • 这将给我留下所有相同列的新表以及表末尾新添加的列。基本上给我留下了另一个架构,因为我需要删除旧列并最终得到新列。
    • 然后在新表建立后执行alter table table_name_copy drop column <old_column>
    • 是的,但这会给我留下一个具有不同架构的新表。基本上是一个新表,末尾有被替换的列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    • 1970-01-01
    • 2011-10-13
    • 2011-09-13
    • 2021-10-19
    • 2010-11-27
    相关资源
    最近更新 更多