如何使用功能索引在 table 上删除行后清理未使用的 space?

How to clean unused space after rows deletion on table with functional indexes?

如何正确清洁tablespace?

例如,我们有一个 table 具有数百万行和功能索引。我们要删除 table.

的大部分

为此我们调用了:从 some_table 中删除....

下一步是什么?

这个顺序正确吗? 1. 删除功能索引。 2. 改变some_table 收缩space。 3. 重新创建函数索引

  1. 创建table新table select * 从旧table 哪里...
  2. 降旧table
  3. 将新table重命名为旧table
  4. 重新创建索引。

正如您可能意识到的(或者您不会特别询问 function-based 索引),您不能简单地:

alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;

尝试这样做将导致:

ORA-10631: SHRINK clause should not be specified for this object

(注意:此限制也适用于位图连接索引。)

很明显,你可以先放弃 FBI...

drop index my_fbi_index;
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
create index my_fbi_index... online;

但这不是在线操作。您的应用程序将在短时间内受到缺少 function-based-index 的影响。

如果您需要在线操作,并且您使用的是 Oracle 12.2 或更高版本,您可以试试这个:

alter table mytable move online;

(alter table...move(无 "online")在 12.2 之前可用,但它不是在线操作,它会删除您的索引段,留下标记为 "unusable" 的索引并需要你重建它们。所以,在 12.2 之前并不是一个好的选择。)

最佳方式

创建一个只包含有效数据的新table,然后重新创建索引,然后删除旧的table.

  • 使用过滤后的数据创建新的 table,其中 table_name 是 table 的名称,filter_text 是以 'WHERE ...' 开头的条件,并且 partitionText 是一个分区子句,如果您有一个用于 table 的子句,例如 RANGE (ENDEDAT) INTERVAL ( NUMTODSINTERVAL(1,''day'') ) ( PARTITION p_first VALUES LESS THAN ( TO_DATE(''01-01-2010'',''dd-MM-yyyy'') ) ) ENABLE ROW MOVEMENT
sqlCommand := 'create table ' || table_name ||'_TMP
        tablespace &TBS_NORMAL_TABLES initrans 32 ' || partitionText ||'
        nologging 
        AS (SELECT * FROM '||table_name|| ' ' ||filter_text||')';

EXECUTE IMMEDIATE sqlCommand;
  • 将属性添加到新 table。

例如,约束、索引...这些可以从 built-in table 中收集,例如 all_constraintsall_indexes。属性的移动也可以自动化,只需要应用一些重命名技巧。

  • 换旧换新table
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||' RENAME TO '||v_table_name||'_OT';
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||'_TP'||' RENAME TO '||v_table_name;
  • 放弃旧的table

execute immediate 'DROP TABLE '||v_table_name||'_OT';

TL;关于缩减和重组的 DR 信息 tables

当考虑在实时生产数据库上归档大量数据时,这里有一些关于我的调查的信息和有用的链接。

自动缩小一些 table 并处理错误的方法

for i in (SELECT obj.owner,obj.table_name,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable, row_movement
        FROM all_tables obj,
        (SELECT table_name, COUNT(rownum) cnt
            FROM user_indexes 
            WHERE index_type LIKE 'FUN%'
        GROUP BY table_name) idx
        WHERE obj.table_name = idx.table_name(+)
        AND obj.owner = &Schemaowner 
        and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
        and NVL(idx.cnt,0) < 1) 
    loop
        BEGIN
            if i.row_movement='ENABLED' then
                execute immediate 'alter table '||i.table_name||' shrink space';
            else
                execute immediate 'alter table '||i.table_name||' enable row movement';
                execute immediate 'alter table '||i.table_name||' shrink space';
                execute immediate 'alter table '||i.table_name||' disable row movement';
            end if;
        DBMS_OUTPUT.PUT_LINE('shrinked table: ' || i.table_name);
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE('error while shrinking table: ' || i.table_name);
                DBMS_OUTPUT.PUT_LINE (SQLERRM);
                DBMS_OUTPUT.PUT_LINE (SQLCODE);

                if SQLCODE=-10635 then
                    for p in (SELECT partition_name ,tablespace_name FROM user_tab_partitions WHERE  table_name = 'SOME_PARTITIONED_TABLE') 
                    loop
                    BEGIN
                        execute immediate 'alter table '||i.table_name||' MOVE PARTITION ' || p.partition_name || ' ONLINE TABLESPACE ' || p.tablespace_name || ' COMPRESS UPDATE INDEXES';
                        DBMS_OUTPUT.PUT_LINE('moved partition: ' || p.partition_name);
                        EXCEPTION
                            WHEN OTHERS THEN
                            DBMS_OUTPUT.PUT_LINE('error while moving partition: ' || p.partition_name);
                            DBMS_OUTPUT.PUT_LINE (SQLERRM);
                            DBMS_OUTPUT.PUT_LINE (SQLCODE);
                            CONTINUE;
                    END;
                    end loop;
                end if;
               CONTINUE;
        END;
    end loop;

有用的选择

可收缩tables

SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes 
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
and obj.owner='YOUR_SCHEMA_OWNER'

使收缩命令无法执行的索引table

SELECT *
FROM all_indexes 
WHERE index_type LIKE 'FUN%'
and owner='YOUR_SCHEMA_OWNER'

可缩table不妥协

SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes 
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
--and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and obj.compression != 'ENABLED'
and obj.table_name not in (SELECT table_name FROM user_tab_partitions WHERE compression = 'ENABLED')
and obj.owner='YOUR_SCHEMA_OWNER'

压缩问题,压缩 tables and/or 个分区

SELECT table_name,compression, compress_for FROM   user_tables WHERE  compression = 'ENABLED'


SELECT table_name,partition_name, compression, compress_for FROM   user_tab_partitions WHERE  compression = 'ENABLED' ORDER BY 1

检查这些 before/after 收缩,测试它的地狱

select segment_name,bytes/1024/1024 as mb,blocks from user_segments where segment_name='TABLE_NAME'

在我的例子中,我创建了一个包含几百万行的 table(未分区),然后删除了其中的 1/3,结果如下:

                ||     BYTES     ||  BLOCKS     ||
Before deletion ||   105250816   ||  12848      ||
After deletion  ||    78774272   ||   9616      ||

调查和side-effects

可能的副作用 https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9536157800346457013

...什么时候应该使用重组: http://www.dba-oracle.com/t_table_fragmentation.htm

...启用行移动而收缩space可以重新排序行(这意味着如果使用基于ROWID工作或选择或类似的东西,那么可能会有一些惊喜)

http://www.dba-oracle.com/t_enable_row_movement.htm

  • 创建您的 table
  • 的副本
  • 降旧table
  • 重命名副本

有一个缺点,在此操作期间(可能需要一些时间)您的应用程序不可用。

所以,你的方法

  1. 删除函数索引。
  2. 改变some_table收缩space.
  3. 再次创建函数索引。

是正确的。

如果你有一个分区 table,请看这个:https://dba.stackexchange.com/questions/162415/how-to-shrink-space-on-table-with-a-function-based-index

我还没有看到有人提出过另一种选择。发出 DELETE,然后什么都不做。为什么我们认为我们需要重建索引?为什么我们认为我们需要调整 table 的大小?如果我们在 DELETE 之后什么都不做,table 的所有范围仍然分配给 table 并将用于未来的插入。如果你有一家零售店并进行清仓销售,导致大量货架空置,你会重建商店以消除 'wasted' space 吗?或者您只是 re-use 新货进货时空空如也的货架?调整 table 的大小有什么好处?充其量,它会将 space 释放回 TS,而不是 OS 文件系统。虽然有一些用例主张调整 table 的大小,但考虑到在大(甚至是大量)DELETE 之后调整大小必然是合理的,这不是自动的。

所以,我的回答是在 DELETE 之后什么也不做(好吧,当然是做 COMMIT!)