Friday, May 28, 2021

Segment advisors on fragmentations

If you perform some sort of cleanup on a table, then the deleted space will be reused upon further insertion, but what if

 

  • That cleanup was the last task that we perform on a table, but not expecting new data to come in.

  • You are performing many full scan queries on that table, but want to make sure they are as efficient as possible.

 

Then there are many benefits of performing shrink on that table to reclaim that space, one of the cool thing about segment advisor is that it will detect, if there are some benefits to be gained by shrinking the segments based on the space management technique of their tablespace attributes.

 

demo@XEPDB1> select tablespace_name,segment_space_management

  2  from dba_tablespaces

  3  where tablespace_name in ('USERS','TS_DATA');

 

TABLESPACE_NAME                SEGMEN

------------------------------ ------

TS_DATA                        MANUAL

USERS                          AUTO

 

So got two tablespace one is ASSM and other is non-ASSM.

 

demo@XEPDB1> create table t_auto

  2  tablespace users

  3  nologging as

  4  select a.*

  5  from dba_objects a,

  6     dba_users u

  7  where rownum <=1000000;

 

Table created.

 

demo@XEPDB1> create table t_manual

  2  tablespace ts_data

  3  nologging as

  4  select a.*

  5  from dba_objects a,

  6     dba_users u

  7  where rownum <=1000000;

 

Table created.

 

we build two different table of same size on each tablespace.

 

demo@XEPDB1> delete from t_auto where mod(object_id,2) =0;

 

500440 rows deleted.

 

demo@XEPDB1> delete from t_manual where mod(object_id,2) =0;

 

500433 rows deleted.

 

demo@XEPDB1> commit;

 

Commit complete.

 

Then we delete all the even rows on them, to make it even more fragmented, and then we use DBMS_ADVISOR packages, to review the findings on allocated and unused spaced on ASSM objects.

 

demo@XEPDB1> declare

  2     l_task_name varchar2(20) :='TEST_TASK';

  3     l_object_id number;

  4  begin

  5     begin

  6             dbms_advisor.delete_task(task_name => l_task_name);

  7             exception

  8                     when others then

  9                     null;

 10     end;

 11     dbms_advisor.create_task( advisor_name=> dbms_advisor.ADV_NAME_SEGMENT,

 12             task_name => l_task_name );

 13     dbms_advisor.create_object( task_name=> l_task_name,

 14             object_type=> 'TABLE',

 15             attr1 =>'DEMO',

 16             object_id => l_object_id,

 17             attr2 =>'T_AUTO' );

 18     dbms_advisor.set_task_parameter( task_name=> l_task_name,

 19             parameter =>'RECOMMEND_ALL',

 20             value=>'TRUE');

 21     dbms_advisor.execute_Task(task_name=> l_task_name);

 22  end;

 23  /

 

PL/SQL procedure successfully completed.

 

demo@XEPDB1> select message,more_info

  2  from user_advisor_findings

  3  where task_name ='TEST_TASK';

 

MESSAGE                                       MORE_INFO

--------------------------------------------- ----------------------------------------

Enable row movement of the table DEMO.T_AUTO  Allocated Space:167772160: Used Space:90

and perform shrink, estimated savings is 7696 811867: Reclaimable Space :76960293:

0293 bytes.

 

But what if the segments sits on non-ASSM tablespaces, on which we cannot perform shrink style operations.

 

demo@XEPDB1> declare

  2     l_task_name varchar2(20) :='TEST_TASK';

  3     l_object_id number;

  4  begin

  5     begin

  6             dbms_advisor.delete_task(task_name => l_task_name);

  7             exception

  8                     when others then

  9                     null;

 10     end;

 11     dbms_advisor.create_task( advisor_name=> dbms_advisor.ADV_NAME_SEGMENT,

 12             task_name => l_task_name );

 13     dbms_advisor.create_object( task_name=> l_task_name,

 14             object_type=> 'TABLE',

 15             attr1 =>'DEMO',

 16             object_id => l_object_id,

 17             attr2 =>'T_MANUAL' );

 18     dbms_advisor.set_task_parameter( task_name=> l_task_name,

 19             parameter =>'RECOMMEND_ALL',

 20             value=>'TRUE');

 21     dbms_advisor.execute_Task(task_name=> l_task_name);

 22  end;

 23  /

 

PL/SQL procedure successfully completed.

 

demo@XEPDB1> select message,more_info

  2  from user_advisor_findings

  3  where task_name ='TEST_TASK';

 

MESSAGE                                       MORE_INFO

--------------------------------------------- ----------------------------------------

Perform re-org on the object T_MANUAL, estima Allocated Space:167772160: Used Space:90

ted savings is 77277135 bytes.                495025: Reclaimable Space :77277135:

 

as you can see the segment advisor will take that into account, and adjust its recommendation accordingly.

 

 

 

 

Monday, May 17, 2021

SQL Transilation - Part II

For a long time, we got a facility called SQL Translator for dynamically translating non-Oracle SQL dialect to Oracle dialect; In addition to that, it is also possible to patch a client application by dynamically changing the sql text at runtime.

 

Will show how this patching can done using SQL Translator feature. Will start with a package.

 

demo@XEPDB1> create or replace package date_translator

  2  is

  3     procedure translate_sql(sql_text in clob ,

  4             translated_text out clob );

  5

  6     procedure translate_error( error_code in binary_integer ,

  7                     translated_code out binary_integer,

  8                     translated_sqlstate out varchar2 );

  9  end;

 10  /

 

Package created.

 

These API’s must be hardcoded, must one should be “translate_sql” and “translate_error” methods and must take the above set of parameters.

 

And here is the package body.

 

demo@XEPDB1> create or replace package body date_translator

  2  is

  3     procedure translate_sql(sql_text in clob ,

  4             translated_text out clob )

  5     as

  6     begin

  7             translated_text := regexp_replace(sql_text,

  8                             'SYSDATE','add_months( current_date, 12*50*-1)',1,0,'i');

  9             translated_text := regexp_replace(translated_text,

 10                             'systimestamp','current_timestamp',1,0,'i');

 11     end;

 12

 13     procedure translate_error( error_code in binary_integer ,

 14                     translated_code out binary_integer,

 15                     translated_sqlstate out varchar2 )

 16     as

 17     begin

 18             null ;

 19     end;

 20  end;

 21  /

 

Package body created.

 

What we are going to do is take input sqltext and having “SYSDATE” function call and translate into fifty years ago (older) from the client date. Similarly change the sqltext having “SYSTIMESTAMP” function calls into client timestamp (i.e. dynamically change server timestamp to client timestamp)

 

Here is how the package is get used in the profile.

 

demo@XEPDB1> begin

  2     dbms_sql_translator.create_profile('DEMO_PROFILE');

  3

  4     dbms_sql_translator.set_attribute(

  5             profile_name=>'DEMO_PROFILE',

  6             attribute_name=> dbms_sql_translator.ATTR_FOREIGN_SQL_SYNTAX,

  7             attribute_value=> dbms_sql_translator.ATTR_VALUE_FALSE );

  8

  9     dbms_sql_translator.set_attribute(

 10             profile_name=>'DEMO_PROFILE',

 11             attribute_name=> dbms_sql_translator.ATTR_TRANSLATOR,

 12             attribute_value=> 'demo.date_translator' );

 13

 14  end;

 15  /

 

PL/SQL procedure successfully completed.

 

So we created a profile and as it should be applied for all sql (and not just for foreign (non-oracle) sql dialects), now we have the new thing called what translator we have to use, that is our new package “date_translator”. So when a sql comes in it calls this package 'demo.date_translator' – and perform the necessary translation - because we set this attribute “dbms_sql_translator.ATTR_TRANSLATOR” for this profile.

 

Let us see this in action.

 

demo@XEPDB1> select deptno,count(*)

  2  from emp

  3  where hiredate < sysdate

  4  group by deptno;

 

DEPTNO   COUNT(*)

------ ----------

    30          6

    10          3

    20          5

 

demo@XEPDB1> alter session set sql_translation_profile=DEMO_PROFILE;

 

Session altered.

 

demo@XEPDB1> select deptno,count(*)

  2  from emp

  3  where hiredate < sysdate

  4  group by deptno;

 

DEPTNO   COUNT(*)

------ ----------

    20          2

 

so post the profile got applied and translated, we got two option to see the final sql text, either pulling the plan cursor cache.

 

demo@XEPDB1> alter session set statistics_level=all;

 

Session altered.

 

demo@XEPDB1> select deptno,count(*)

  2  from emp

  3  where hiredate < sysdate

  4  group by deptno;

 

DEPTNO   COUNT(*)

------ ----------

    20          2

 

demo@XEPDB1> select * From table( dbms_xplan.display_cursor(format=>'allstats last'));

 

PLAN_TABLE_OUTPUT

---------------------------------------------------------------------------

SQL_ID  8u8mq3pb9pjhy, child number 1

-------------------------------------

select deptno,count(*) from emp where hiredate < add_months(

current_date, 12*50*-1) group by deptno

 

Plan hash value: 4067220884

 

---------------------------------------------------------------------------

| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   |

---------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |

|   1 |  HASH GROUP BY     |      |      1 |      2 |      1 |00:00:00.01 |

|*  2 |   TABLE ACCESS FULL| EMP  |      1 |      3 |      2 |00:00:00.01 |

---------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   2 - filter("HIREDATE"<ADD_MONTHS(CURRENT_DATE,-600))

 

Alternatively, by using “expand_sql_text” method from “dbms_utility” API’s.

 

demo@XEPDB1> variable x clob

demo@XEPDB1> declare

  2     l_txt long;

  3  begin

  4     l_txt := 'select deptno,count(*)

  5                     from emp

  6                     where hiredate < sysdate

  7                     group by deptno ';

  8   dbms_utility.expand_sql_text( l_txt,:x );

  9  end;

 10  /

 

PL/SQL procedure successfully completed.

 

demo@XEPDB1> print x

 

X

--------------------------------------------------------------------------------

SELECT "A1"."DEPTNO" "DEPTNO",COUNT(*) "COUNT(*)" FROM "DEMO"."EMP" "A1" WHERE "

A1"."HIREDATE"<ADD_MONTHS(CURRENT_DATE,12*50*(-1)) GROUP BY "A1"."DEPTNO"

 

Therefore, with this we got two great things with SQL Translation profile.

 

·         Grab an entire sql and replace that with another sql, but that still need not to be a non-oracle sql’s too.

 

·         Using translation package, we can pick and choose elements of incoming sql and dynamically replace them with whatever we like (in this example we replace sysdate and systimestamp with current_date and current_timestamp).