在 Oracle 中更新表

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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 脚本,我会在测试数据库中对其进行双重检查和测试。但为了示例,我将编辑我的答案。 您是否提到“想要重新排列表格以便释放的空白再次可用”是通过什么方式完成的?

以上是关于在 Oracle 中更新表的主要内容,如果未能解决你的问题,请参考以下文章

为啥 oracle 不支持在单个查询中更新多个表?

Oracle:如何在模式中找到上次更新(任何表)的时间戳?

在 Oracle 中更新表

在 Oracle 更新语句中使用子查询而不是表名

关于oracle 多表关联更新的问题

oracle怎么用一个表的多个字段数据更新另一个表相应的字段中