【问题标题】:Update a table in Oracle在 Oracle 中更新表
【发布时间】:2020-05-05 23:08:22
【问题描述】:

我需要将 CHAR 类型的表的列更改为 VARCHAR2,但他们要求我自动执行此操作,因为它们是 100 个表。在 Oracle 中更新表的所有列而不需要逐列放置的一种方法是使用从 user_tables 的视图中提取列的方法,但是这些表在执行数据时具有数据,将 CHAR 处理的空格更改为权利也传递给 VARCHAR2 我需要的是更新应用 RTRIM 的表的数据,我用更新表 set column = rtrim (column);但他们问我更新不是逐列而是在整个表中,所以它只是一个查询,有没有办法实现它?我用谷歌搜索,只找到我已经使用的语法

【问题讨论】:

    标签: oracle


    【解决方案1】:

    您可以使用 SQL 生成必要的语句。一个最小的例子:

    CREATE TABLE t0 (i INT);
    CREATE TABLE t1 (a CHAR(10), b NUMBER, c VARCHAR2(30), d CHAR(40));
    INSERT INTO  t1 VALUES('a',1,'a','a');
    CREATE TABLE t2 (x CHAR(10), y CHAR(20 CHAR), z CHAR(30 BYTE));
    INSERT INTO  t2 VALUES('b','b','b');
    
    SELECT 'UPDATE ' || table_name ||' SET ' || 
           LISTAGG(column_name||'=RTRIM('||column_name||')', ', ')
           WITHIN GROUP (ORDER BY column_id) || '; COMMIT;' as sql
      FROM user_tab_columns
     WHERE data_type='CHAR'
     GROUP BY table_name
     ORDER BY table_name;
    

    将产生以下语句:

    UPDATE T1 SET A=RTRIM(A), D=RTRIM(D); COMMIT;
    UPDATE T2 SET X=RTRIM(X), Y=RTRIM(Y), Z=RTRIM(Z); COMMIT;
    

    同样,

    SELECT 'ALTER TABLE '||table_name||' MODIFY ('||LISTAGG(
            column_name||
          ' VARCHAR2('||data_length||' '||DECODE(char_used,'B','BYTE','C','CHAR')||')', ', ') 
            WITHIN GROUP (ORDER BY column_id) ||');' as sql
      FROM user_tab_columns
     WHERE data_type='CHAR'
     GROUP BY table_name
     ORDER BY table_name;
    

    会产生

    ALTER TABLE T1 MODIFY (A VARCHAR2(10 BYTE), D VARCHAR2(40 BYTE));
    ALTER TABLE T2 MODIFY (X VARCHAR2(10 BYTE), Y VARCHAR2(80 CHAR), Z VARCHAR2(30 BYTE));
    

    请彻底测试您的脚本,让其他人查看脚本并在运行之前备份数据库。

    编辑:

    您可以将语句包装在 PL/SQL 过程中:

    CREATE OR REPLACE PROCEDURE p AS
    BEGIN
      FOR r IN (
         SELECT 'UPDATE ' || table_name ||' SET ' || 
                LISTAGG(column_name||'=RTRIM('||column_name||')', ', ')
                WITHIN GROUP (ORDER BY column_id) as stmt
           FROM user_tab_columns
          WHERE data_type='CHAR'
          GROUP BY table_name
          ORDER BY table_name)
      LOOP
        DBMS_OUTPUT.PUT_LINE(r.stmt);
        EXECUTE IMMEDIATE r.stmt;
        COMMIT;
      END LOOP;
    
      FOR r IN (
         SELECT 'ALTER TABLE '||table_name||' MODIFY ('||LISTAGG(
                 column_name||
               ' VARCHAR2('||data_length||' '||DECODE(char_used,'B','BYTE','C','CHAR')||')', ', ') 
                 WITHIN GROUP (ORDER BY column_id) ||')' as stmt
           FROM user_tab_columns
          WHERE data_type='CHAR'
          GROUP BY table_name
         ORDER BY table_name)
      LOOP
        DBMS_OUTPUT.PUT_LINE(r.stmt);
        EXECUTE IMMEDIATE r.stmt;
      END LOOP;
    END p;
    /
    

    编辑:

    编辑:

    哦,我忘记了,您可能需要重新组织表格以使释放的空白空间再次可用。一个最小的例子:

    CREATE TABLE t (c CHAR(2000));
    CREATE INDEX i ON t(c);
    INSERT INTO t SELECT 'x' FROM all_objects;
    67,114 rows inserted.
    

    表184 MB,索引240 MB:

    SELECT segment_type, segment_name, round(bytes/1024/1024) AS mb 
      FROM user_segments WHERE segment_name IN ('T','I');
    
    INDEX I 240
    TABLE T 184
    

    现在,如果你转换和 rtrim 表格,表格和索引仍然是相同的大小:

    ALTER TABLE t MODIFY (c VARCHAR2(2000));
    UPDATE t SET c = RTRIM(c);
    
    INDEX I 240
    TABLE T 184
    

    只有在重新组织表之后,Oracle 才会删除不再需要的空间。该表现在为 1 MB,索引为 2 MB:

    ALTER TABLE t MOVE;
    ALTER INDEX I REBUILD;
    
    INDEX I 2
    TABLE T 1
    

    【讨论】:

    • 谢谢,您的回答对我很有帮助,您知道我是否可以在 PL/SQL 中执行此操作吗?
    • 当然。就个人而言,我不会对此感到满意,我宁愿生成一个带有语句的 SQL 脚本,我会在测试数据库中对其进行双重检查和测试。但为了示例,我将编辑我的答案。
    • 您是否提到“想要重新排列表格以便释放的空白再次可用”是通过什么方式完成的?
    猜你喜欢
    • 2011-05-04
    • 1970-01-01
    • 2022-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多