While everyone is aware of the issues of mixing EHCC compression and OLTP type activities, I had a customer who was interested in finding out what happens upon adding a column to a table that has EHCC compression enabled on it.
As I could not see any definitive statements in the documentation on this particular scenario I ran up some tests to see the behaviour.
First of all they are using partitioning by date range, so we create a partitioned table:
SQL: db01> create table t_part ( username varchar2(30), user_id number, created date ) partition by range (created) ( partition p_2009 values less than (to_date('31-DEC-2009', 'dd-MON-YYYY')) tablespace users, partition p_2010 values less than (to_date('31-DEC-2010', 'dd-MON-YYYY')) tablespace users, partition p_2011 values less than (to_date('31-DEC-2011', 'dd-MON-YYYY')) tablespace users, partition p_2012 values less than (to_date('31-DEC-2012', 'dd-MON-YYYY')) tablespace users ) / Table created
The customer is particularly interested in using partitioning for ILM type scenarios in that they will compress historical partitions but not more up-to-date ones. Lets enable HCC compression on the table and check that it’s on:
SQL: db01> alter table t_part compress for query high / Table altered SQL: db01> select table_name, partition_name, compression, compress_for from all_tab_partitions where table_name='T_PART' / TABLE_NAME PARTITION_NAME COMPRESS COMPRESS_FOR ------------------------------ ------------------------------ -------- ------------ T_PART P_2009 ENABLED QUERY HIGH T_PART P_2010 ENABLED QUERY HIGH T_PART P_2011 ENABLED QUERY HIGH T_PART P_2012 ENABLED QUERY HIGH
Lets insert some data and check that the actual row is compressed (thanks to Kerry Osborne)
SQL: db01>; insert /*+ APPEND */ into t_part select * from all_users / 3008 rows created SQL: db01> commit / Commit complete SQL: db01> select max(rowid) from t_part / MAX(ROWID) ------------------ AAAexSAANAAHGoUAAN SQL: db01> select decode( DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), 1, 'No Compression', 2, 'Basic/OLTP Compression', 4, 'HCC Query High', 8, 'HCC Query Low', 16, 'HCC Archive High', 32, 'HCC Archive Low', 'Unknown Compression Level') compression_type from dual; Enter value for rowid: AAAexSAANAAHGoUAAN old 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), new 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', 'AAAexSAANAAHGoUAAN'), COMPRESSION_TYPE ------------------------- HCC Query High
So we are confident we have a row that is compressed. Now we add a new column to the table and give it a default value, we then check again what compression the row has:
SQL: db01> alter table t_part add city varchar2(30) default 'Oxford' / Table altered. select decode( DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), 2 3 1, 'No Compression', 4 2, 'Basic/OLTP Compression', 5 4, 'HCC Query High', 6 8, 'HCC Query Low', 7 16, 'HCC Archive High', 32, 'HCC Archive Low', 'Unknown Compression Level') compression_type from dual; Enter value for rowid: AAAexSAANAAHGoUAAN old 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), new 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', 'AAAexSAANAAHGoUAAN'), COMPRESSION_TYPE ------------------------- Basic/OLTP Compression
Oh Dear! Our compression has changed.
This maybe is not that surprising. But what if you have a requirement to add a column but with no default value, and you just want to update more recent records, can we avoid downgrading all records from HCC compression?
So we are using the same table and data as before. We will focus on two rows, one in the 2011 partition and one in the 2012 partition.
SQL: db01> select max(rowid) from t_part where created > TO_DATE('31-Dec-2010', 'DD-MM-YYYY') and created < TO_DATE('01-Jan-2012', 'DD-MM-YYYY'); MAX(ROWID) ------------------ AAAezbAAHAAFwIKAE/ SQL: db01> select decode( DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), 1, 'No Compression', 2, 'Basic/OLTP Compression', 4, 'HCC Query High', 8, 'HCC Query Low', 16, 'HCC Archive High', 32, 'HCC Archive Low', 'Unknown Compression Level') compression_type from dual; Enter value for rowid: AAAezbAAHAAFwIKAE/ old 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), new 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', 'AAAezbAAHAAFwIKAE/'), COMPRESSION_TYPE ------------------------- HCC Query High SQL: db01> select max(rowid) from t_part where created > TO_DATE('31-Dec-2011', 'DD-MM-YYYY') and created < TO_DATE('31-Dec-2012', 'DD-MM-YYYY'); MAX(ROWID) ------------------ AAAezcAAHAAHdoSADf SQL:xldnc911001hdor:(SMALLDB1):PRIMARY> select decode( DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), 1, 'No Compression', 2, 'Basic/OLTP Compression', 4, 'HCC Query High', 8, 'HCC Query Low', 16, 'HCC Archive High', 32, 'HCC Archive Low', 'Unknown Compression Level') compression_type from dual; Enter value for rowid: AAAezcAAHAAHdoSADf old 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), new 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', 'AAAezcAAHAAHdoSADf'), COMPRESSION_TYPE ------------------------- HCC Query High
Now we add a column to the table and update the records in only the 2012 partition:
SQL: db01> alter table t_part add city varchar2(30); Table altered. SQL: db01> update t_part set city='Oxford' where created > to_date('31-Dec-2011', 'DD-MM-YYYY'); 448 rows updated. SQL: db01> commit; Commit complete.
And now we again check the compression status of our two rows:
SQL: db01> select decode( DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), 1, 'No Compression', 2, 'Basic/OLTP Compression', 4, 'HCC Query High', 8, 'HCC Query Low', 16, 'HCC Archive High', 32, 'HCC Archive Low', 'Unknown Compression Level') compression_type from dual; Enter value for rowid: AAAezbAAHAAFwIKAE/ old 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), new 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', 'AAAezbAAHAAFwIKAE/'), COMPRESSION_TYPE ------------------------- HCC Query High SQL: db01> select decode( DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), 1, 'No Compression', 2, 'Basic/OLTP Compression', 4, 'HCC Query High', 8, 'HCC Query Low', 16, 'HCC Archive High', 32, 'HCC Archive Low', 'Unknown Compression Level') compression_type from dual; Enter value for rowid: AAAezcAAHAAHdoSADf old 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', '&rowid'), new 2: DBMS_COMPRESSION.GET_COMPRESSION_TYPE ( 'SYS', 'T_PART', 'AAAezcAAHAAHdoSADf'), COMPRESSION_TYPE ------------------------- Basic/OLTP Compression
So that is great, we have a way of evolving table definitions without having to suffer the whole set of historical data to not be in HCC compression.
Something seems wrong here. First, adding a column with a default value is a dictionary only change since 11g. It never changes the rows, even with HCC. Second, HCC rows are immutable (on disk) – they can not be changed, they must be migrated to a new format if modified. This would cause rowid changes. See my example: http://structureddata.org/2011/01/11/making-the-most-of-oracle-exadata-a-technical-review/
My theory is that there is a bug in dbms_compression.get_compression_type for this specific case. No rows are changing, it’s simply a reporting issue. See if you can show some evidenced that the on disk format is changing, other than dbms_compression.
Hi Greg,
Thanks for reading and providing useful feedback.
Will look to dump some blocks.
Jason
Hi Greg,
It is my understanding that the 11g change you refer to applies only when a NOT NULL constraint is used. From the 11.1 New Features docs –
1.2.9.2 Enhanced ADD COLUMN Functionality
Default values of columns are maintained in the data dictionary for columns specified
as NOT NULL.
It would be interesting to see how the output of get_compression_type differs if the column is added with a NOT NULL constraint.
Hi Matthew,
Interesting. Will try and test and block dump this also.
This is running on 11.2.0.2 BP 14. With storage server 11.2.2.4.2
Jason.
@matthewdba
This is exactly what I was referring to. My comment should have included the not null part. Adding a column with a default value does not change current rows, unless the column is not null, in which case, in 11g it is still a dictionary only change.
Of course, the blog entry did not have a not null included!
I’ve tested with not null and the compression_type records it correctly as unchanged.
Having dumped the blocks without not null the blocks are changed.
Compression_type seems correct in all circumstances.
Jason
Your are findings are accurate — I just reproduced this.
Three options to add columns to a populated table are:
1) adding a column
2) adding a column with “default X not null”
3) adding a column with “default X”
The first two work as expected — they do not change any rows on disk. The third does migrate rows from HCC compression because it is not a dictionary only change.
I filed bug 14087817 for a paper trail. In discussions with db dev peeps, it looks like it is a known issue and is being addressed.
BTW, an easy way to check this is simply gather stats on the table (before/after), and query the number of blocks in the segment. Block dumps work, but may be overkill. š