This is the 29th day of my participation in the August Wenwen Challenge.More challenges in August
preface
Why convert an ordinary table to a partitioned table? What are some ways to do that?
- As one of the three components of Oracle, partitioned table plays an important role in Oracle database.
What are the advantages of partitioned tables?
- Common table to partitioned table: The application is not aware, DML statements can access partitioned table without modification.
- High availability: The unavailability of some partitions does not affect the use of the entire partition table.
- Easy management: You can perform DDL operations on partitions separately, including rebuilding indexes or expanding partitions, without affecting the use of partitioned tables.
- Reduce OLTP system resource contention: because DML operates over many segments.
The online redefinition method can be used to convert partitioned tables online. The process is simple and the conversion can be performed quickly.
Introduce a,
DBMS_REDEFINITION (online redefinition) :
- Supported Database versions: Oracle Database-Enterprise Edition – Version 9.2.0.4 and later
- Online redefinition is done through materialized views.
Some restrictions for using online redefinition:
- There must be enough table space to hold twice as much data as the table.
- Primary key columns cannot be modified.
- Tables must have primary keys.
- The online redefinition must be performed under the same user.
- The tables under the SYS and SYSTEM users cannot be redefined online.
- Nologging cannot be used for online redefinition.
- If the intermediate table has new columns, it cannot have a NOT NULL constraint
DBMS_REDEFINITION package:
- ABSORT_REDEF_TABLE: clears redefinition errors and suspends redefinition;
- CAN_REDEF_TABLE: checks whether the table can be redefined. If the stored procedure is successfully executed, the table can be redefined.
- COPY_TABLE_DEPENDENTS: Synchronize index and dependent object (index, constraint, trigger, permission, etc.);
- FINISH_REDEF_TABLE: online redefinition is complete.
- REGISTER_DEPENDENTS_OBJECTS: Register a dependent object such as an index, constraint, trigger, etc.
- START_REDEF_TABLE: starts online redefinition.
- SYNC_INTERIM_TABLE: synchronizes data incrementally.
- UNREGISTER_DEPENDENT_OBJECT: Does not register a dependent object, such as an index, constraint, or trigger.
Second, the actual combat
Test environment database installation:
- 11G:./ oracleshellinstall. sh -i 10.211.55.111
For more details on how scripts are used, subscribe to the column:Oracle install scripts with one click.
How to obtain scripts:
- GitHub Keep updating at 🔥
- Gitee Keep updating at 🔥
1 Build test data
Create test tablespace and user:
sqlplus / as sysdba
create tablespace PAR;
create user par identified by par;
grant dba to par;
Copy the code
Create test table:
sqlplus par/par
create table lucifer(
id number(8) PRIMARY KEY,
name varchar2(20) not null,
par_date date)
tablespace PAR;
comment on table lucifer is 'lucifer table';
comment on column lucifer.name is 'name';
comment on column lucifer.par_date is 'Partition date';
create index id_name on lucifer(name) tablespace par;
Copy the code
Insert test data:
sqlplus par/par
begin
for i in0.. 24 loop insert into lucifer values (i,'lcuifer_' || i,
add_months(to_date('2021-1-1'.'yyyy-mm-dd'), i));
end loop;
commit;
end;
/
Copy the code
As you can see, the test data has been constructed, and it’s time to go live.
2 Check whether the definition can be modified
Select * from table where primary key (s) exists;
sqlplus / as sysdba
## Check the primary key
select cu.* from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'P' and au.table_name = 'LUCIFER';
Copy the code
Confirm if redefinition can be done without primary key using rowid:
sqlplus / as sysdba
exec dbms_redefinition.can_redef_table('PAR'.'LUCIFER');
Copy the code
Execution without an error indicates that the table can be redefined online.
Create intermediate table (partitioned table structure)
Create a partitioned table structure with PL/SQL package:
sqlplus par/par
BEGIN
ctas_par(p_tab => 'lucifer',
p_part_colum => 'par_date',
p_part_nums => 24,
p_tablespace => 'par');
END;
/
Copy the code
Note: PL/SQL packages refer to:Oracle common tables are converted to partitioned tables on a monthly basis, and partitioned tables are generated by one-click PLSQL packages
Create intermediate partition table lucifer_PAR:
create table lucifer_par
(
id NUMBER(8),
name VARCHAR2(20),
par_date DATE
)
partition BY RANGE(par_date)(
partition lucifer_P202101 values less than (TO_DATE('2021-02-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202102 values less than (TO_DATE('2021-03-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202103 values less than (TO_DATE('2021-04-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202104 values less than (TO_DATE('2021-05-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202105 values less than (TO_DATE('2021-06-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202106 values less than (TO_DATE('2021-07-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202107 values less than (TO_DATE('2021-08-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202108 values less than (TO_DATE('2021-09-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202109 values less than (TO_DATE('2021-10-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202110 values less than (TO_DATE('2021-11-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202111 values less than (TO_DATE('2021-12-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202112 values less than (TO_DATE('2022-01-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202201 values less than (TO_DATE('2022-02-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202202 values less than (TO_DATE('2022-03-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202203 values less than (TO_DATE('2022-04-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202204 values less than (TO_DATE('2022-05-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202205 values less than (TO_DATE('2022-06-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202206 values less than (TO_DATE('2022-07-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202207 values less than (TO_DATE('2022-08-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202208 values less than (TO_DATE('2022-09-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202209 values less than (TO_DATE('2022-10-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202210 values less than (TO_DATE('2022-11-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202211 values less than (TO_DATE('2022-12-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_P202212 values less than (TO_DATE('2023-01-01 00:00:00'.'SYYYY-MM-DD HH24:MI:SS'.'NLS_CALENDAR=GREGORIAN')) tablespace par,
partition lucifer_MAX values less than (maxvalue) tablespace par)
enable row movement
tablespace par;
Copy the code
As above, if unique indexes and constraints are not added, they are automatically copied, and the middle table of the partitioned table structure has been generated.
4 Check whether row migration is enabled in the intermediate table
select row_movement from dba_tables where table_name='LUCIFER' and owner='PAR';
select row_movement from dba_tables where table_name='LUCIFER_PAR' and owner='PAR';
Copy the code
5 Collect table statistics
To ensure accurate data, collect statistics before you start:
sqlplus / as sysdba
exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8');exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER_PAR',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8');Copy the code
6 Start online redefinition
sqlplus / as sysdba
EXEC DBMS_REDEFINITION.START_REDEF_TABLE('PAR'.'LUCIFER'.'LUCIFER_PAR');
Copy the code
7 Copy table attributes to exclude indexes
Select automatic replication table property to manually create local index (Local)
- Advantages: You only need to focus on index omissions, and do not need to focus on triggers, permissions, constraints, and other dependencies.
- Disadvantages: Manually create indexes and manually rename indexes.
May refer to:COPY_TABLE_DEPENDENTS for Oracle online redefine
sqlplus par/par
SET SERVEROUTPUT ON
DECLARE
l_errors NUMBER;
BEGIN
DBMS_REDEFINITION.copy_table_dependents(
uname => USER,
orig_table => 'LUCIFER',
int_table => 'LUCIFER_PAR',
copy_indexes => 0,
copy_triggers => TRUE,
copy_constraints => TRUE,
copy_privileges => TRUE,
ignore_errors => FALSE,
num_errors => l_errors,
copy_statistics => FALSE,
copy_mvlog => FALSE);
DBMS_OUTPUT.put_line('Errors=' || l_errors);
END;
/
Copy the code
If no error is reported during execution, it indicates normal.
8 Create local indexes for intermediate tables
Alter table LUCIFER_PAR create index:
create index ID_NAME_PAR on LUCIFER_PAR(NAME) tablespace PAR local parallel 8;
Copy the code
Note: The index name must be different from the original index name.
9 Cancel index parallelism
If parallel creation is enabled during index creation, the index parallelism must be cancelled:
sqlplus / as sysdba
select 'alter index '||owner||'. '||index_name||' noparallel; '
from dba_indexes
where table_name = 'LUCIFER_PAR' and owner= 'PAR';
Copy the code
10 Synchronize data (reduces table lock time at the end of the redefinition process)
sqlplus / as sysdba
BEGIN
dbms_redefinition.sync_interim_table(
uname => 'PAR',
orig_table => 'LUCIFER',
int_table => 'LUCIFER_PAR');
END;
/
Copy the code
Note: This step is done to reduce the amount of time the table is locked at the end of the redefinition.
11 Collect statistics about the intermediate table
To prepare for the following synchronization data, collect intermediate table statistics:
sqlplus / as sysdba
exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER_PAR',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8');Copy the code
12 End redefinition (End redefinition requires locking the table. The specific time depends on the size of the table.)
sqlplus / as sysdba
BEGIN
dbms_redefinition.finish_redef_table(
uname => 'PAR',
orig_table => 'LUCIFER',
int_table => 'LUCIFER_PAR');
END;
/
Copy the code
13 Check whether the partition table has been converted
sqlplus par/par
select owner,table_name,partitioned from user_tables where table_name in ('LUCIFER'.'LUCIFER_PAR');
Copy the code
As above, the LUCIFER table has been redefined online to a partitioned table structure.
14 Manually rename the index
1. At this time, the original table name has been converted to an intermediate table. Rename the original table to another name
sqlplus / as sysdba
ALTER index PAR.ID_NAME RENAME TO ID_NAME_BAK;
Copy the code
2. Rename The index of the new partitioned table. Because the index name of the new partitioned table is the index name of the intermediate table, you need to manually rename the index
sqlplus / as sysdba
ALTER index PAR.ID_NAME_PAR RENAME TO ID_NAME;
Copy the code
15 Check whether invalid indexes exist
sqlplus / as sysdba
SELECT owner index_owner, index_name, index_type,'N/A' partition_name,status,table_name,tablespace_name,
'alter index '||owner||'. '||index_name||' rebuild; ' rebuild_index
FROM dba_indexes
WHERE status = 'UNUSABLE'
UNION ALL
SELECT a.index_owner,a.index_name,b.index_type,a.partition_name,a.status,b.table_name,a.tablespace_name,
'alter index '||a.index_owner||'. '||a.index_name||' rebuild partition '||a.partition_name||' ;' rebuild_index
FROM dba_ind_partitions a, dba_indexes b
WHERE a.index_name = b.index_name
AND a.index_owner = b.owner
AND a.status = 'UNUSABLE'
UNION ALL
SELECT owner index_owner,a.index_name,b.index_type,'N/A' partition_name,a.status,b.table_name,NULL,
'alter index '||a.index_owner||'. '||a.index_name||' rebuild subpartition '||a.subpartition_name||'; ' rebuild_index
FROM dba_ind_subpartitions a, dba_indexes b
WHERE a.index_name = b.index_name
AND a.index_owner = b.owner
AND a.status = 'UNUSABLE';
Copy the code
16 Check whether row_movement is enabled after the switchover
sqlplus / as sysdba
select owner,table_name,row_movement from dba_tables where table_name in ('LUCIFER'.'LUCIFER_PAR') and owner='PAR';
Copy the code
17 Check invalid objects
## Invalid object compiledsqlplus / as sysdba @? /rdbms/admin/utlrp.sql select'alter '||object_type||' '||owner||'. '||object_name||' compile; '
from dba_objects t
where t.status = 'INVALID' order by 1;
Copy the code
18 Collect statistics
sqlplus / as sysdba
exec dbms_stats.gather_table_stats(ownname => 'PAR',tabname => 'LUCIFER',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE,degree => '8');Copy the code
19 Insert test data
sqlplus par/par
begin
for i in100.. 124 loop insert into lucifer values (i,'lcuifer_' || i,
add_months(to_date('2021-5-1'.'yyyy-mm-dd'), i));
end loop;
commit;
end;
/
Copy the code
20 Query data distribution in the partition table
sqlplus par/par
SELECT COUNT(*) FROM LUCIFER;
SELECT * FROM LUCIFER PARTITION(LUCIFER_P202101);
SELECT * FROM LUCIFER PARTITION(LUCIFER_P202201);
SELECT * FROM LUCIFER PARTITION(LUCIFER_MAX);
Copy the code
You can see that the data is already evenly distributed among the different subpartitions by date. At this point, the online redefinition is complete and the partitioned table has been successfully transformed.
Refer to MOS documentation:
- How To Partition Existing Table Using DBMS_REDEFINITION (Doc ID 472449.1)
- COPY_TABLE_DEPENDENTS for Oracle online redefine
This is the end of sharing ~
If you think the article is helpful to you, please like it, favorites it, pay attention to it, comment on it, and support it four times with one button. Your support is the biggest motivation for my creation.