Friday, December 21, 2018

How to create a materialized view with query rewrite enabled


In this example, I am creating a materialized view to support queries against a table with 20 million rows, which are looking at the maximum sequence number within a range of dates.

Oracle describes the benefits of Materialized views like this:

"One of the major benefits of creating and maintaining materialized views is the ability to take advantage of query rewrite, which transforms a SQL statement expressed in terms of tables or views into a statement accessing one or more materialized views that are defined on the detail tables."

Since I want the materialized view to be refreshed every time a new row is commited to the base table, I need to specify that the mview should use "fast refresh".

First, create a materialized view log to store changed rows in the base tables that your materialized views are built on. This is required when using FAST REFRESH:
CREATE MATERIALIZED VIEW LOG ON DATE_TO_SEQNO
TABLESPACE DATA1
CACHE
ENABLE ROW MOVEMENT
WITH ROWID (SEQNO, RDATE)
INCLUDING NEW VALUES;

Then, create the materialized view. Query rewrites are disabled by default, so it must be explicitly stated:
CREATE MATERIALIZED VIEW SEQ_AGGR_INFO
SEGMENT CREATION IMMEDIATE
TABLESPACE DATA1
CACHE
ENABLE ROW MOVEMENT
ROW STORE COMPRESS ADVANCED
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE
AS
select RDATE, count(SEQNO) as antall, max(SEQNO) as makssekvens
from DATE_TO_SEQNO
group by dato
order by 1, 2;

Verify the mview's existence:
SELECT mview_name,rewrite_enabled,refresh_mode,refresh_method, last_refresh_type, last_refresh_date,last_refresh_end_time,staleness,compile_state
FROM USER_MVIEWS;

MVIEW_NAME REWRITE_ENABLED REFRESH_MODE REFRESH_METHOD LAST_REFRESH_TYPE LAST_REFRESH_DATE LAST_REFRESH_END_TIME STALENESS COMPILE_STATE
SEQ_AGGR_INFO Y COMMIT FAST COMPLETE 18.01.2019 13:58:24 18.01.2019 13:58:24 FRESH VALID

Verify the mview log's existence:
SELECT master,log_table,rowids, primary_key,filter_columns, include_new_values
FROM USER_MVIEW_LOGS;

MASTER LOG_TABLE ROWIDS PRIMARY_KEY FILTER_COLUMNS INCLUDE_NEW_VALUES
DATE_TO_SEQNO MLOG$_DATE_TO_SEQNO YES NO YES YES


The following query will now use the materialized view, due to the QUERY REWRITE directive used in the creation of the mview:
var B1 VARCHAR2(10);
EXEC :B1 := to_date('01.01.2018','dd.mm.yyyy');
set lines 200
set autot on
set timing on
select max(seqno)
from DATE_TO_SEQNO
where rdate = :B1;

The explain plan verifies it:
MAX(SEKVENSNUMMER)
------------------
          18580449

Elapsed: 00:00:00.05

Execution Plan
----------------------------------------------------------
Plan hash value: 1856567295

--------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                        |     1 |    14 |   243   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE               |                        |     1 |    14 |            |          |
|*  2 |   MAT_VIEW REWRITE ACCESS FULL| SEQ_AGGR_INFO          |     1 |    14 |   243   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("SEQ_AGGR_INFO"."RDATE"=:B1)

To drop the materialized view and its log object:
drop materialized view log on DATE_TO_SEQNO;
drop materialized view SEQ_AGGR_INFO;

Sources:
Create Materialized View Log

Create Materialized view

Datawarehousing guide

Thursday, December 20, 2018

How to generate a script to rebuild unusable indexes



set trimspool on
set lines 200
set pages 0
set heading off
set verify off
set feedback off
set echo off
spool rebuild_stmts.sql

select 'spool rebuild_stmts.log' from dual;
SELECT 'alter index ' || idx.table_owner || '.' || idx.index_name || ' rebuild ' || ' ONLINE;'
FROM dba_indexes idx
where idx.status = 'UNUSABLE'
ORDER BY idx.table_owner, idx.index_name
/
select 'exit' from dual;

exit

Wednesday, December 19, 2018

How to find the version of postgres installed on a server


$ su - postgres
Last login: Tue Dec 18 23:03:07 CET 2018
$ pg_config --version
PostgreSQL 11.1

Using psql:
[postgres@til0drift-dbteam-sandbox-pgsql01 ~]$ psql
psql (11.2)
Type "help" for help.

postgres=# select version();
                                                 version
---------------------------------------------------------------------------------------------------------
 PostgreSQL 11.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
(1 row)

Monday, December 10, 2018

How to send a file to a specific directory using wget

Here demonstrated while getting a file from our RH satellite server:

wget -P /home/oracle/myfiles http://satellite/oracle/cloningscripts/post_clone.sql

If the directory /home/oracle/mfiles is not present, it will be created.

Friday, December 7, 2018

How to create the SCOTT user

To create the user SCOTT, run the script

$ORACLE_HOME/rdbms/admin/utlsampl.sql

Observe that user SCOTT will be created with the following statement:

GRANT CONNECT,RESOURCE,UNLIMITED TABLESPACE TO SCOTT IDENTIFIED BY tiger;

Note that if the default profile is using a password verification function, the creation will fail.

To work around, exchange default password "tiger" with something different that will honor the verification function, for example "T123G456R".

Remember to set
SET TERMOUT ON
SET ECHO ON
at the top, if you want to see the script output.

Wednesday, November 28, 2018

Changes in privilege "SELECT ANY DICTIONARY" in Oracle 12c


From version 12.1 and onwards, Oracle has introduced some changes to enhance security when granting the system privilege "SELECT ANY DICTIONARY".

In the New Features guide for version 12.1, the authors explain:

The SELECT ANY DICTIONARY privilege no longer permits access to security sensitive data dictionary tables DEFAULT_PWD$, ENC$, LINK$, USER$, USER_HISTORY$, and XS$VERIFIERS.

This change increases the default security of the database by not allowing access to a subset of data dictionary tables through the SELECT ANY DICTIONARY privilege.

The access to USER$ has also been excempt when granting the system privilege SELECT ANY TABLE and the role SELECT_CATALOG_ROLE, but I have not yet found the documentation that verifies this.

Monday, November 19, 2018

How to use rman to create a backup-based clone on a remote server


It has been a while since I needed to use this technique, since we rely on storage snapshot clones these days. Sometimes though, a good old-fashioned rman clone based on backups is the only way to solve a problem.
Since I always avoid working on the production server during cloning, most steps are done logged onto the auxiliary server.

The method I used is based on "Backup-Based Duplication Without a Target Database and Recovery Catalog Connection"
Here is what I did:

1. On the production server, backup of the source database using RMAN:
rman target / nocatalog
backup database plus archivelog;

From this point and onwards, everything is done while logged onto the destination server.

2. Get the files from the source server:
ssh testserver
cd /u05
mkdir bup
cd bup
scp -r prodserver:/u05/flash_recovery_area/PRODDB01/2018_11_16/* .

3. Prepare the auxiliar pfile. Note that the parameters I use exceeds the actual required parameters.
But since the auxiliary instance will replace an already existing database instance, which is already tuned and has the correct memory parameters, I choose to include them. Notice also the db_file_name_convert and log_file_name_convert parameters. They control where rman will place the files during the restore process.
cd $ORACLE_HOME/dbs
vi inittest1.ora

Add the following:

*.db_name='test1'
*.db_unique_name='test1'
*.audit_file_dest='/u01/oracle/admin/test1/adump'
*.audit_trail='DB'
*.compatible='12.2.0'
*.control_files='/u02/oradata/test1/control01.ctl','/u04/fra/test1/control02.ctl'
*.db_block_size=8192
*.db_file_name_convert='proddb01','test1'
*.log_file_name_convert='proddb01','test1'
*.db_recovery_file_dest='/u05/flash_recovery_area/test1'
*.db_recovery_file_dest_size=1000G
*.diagnostic_dest='/u01/oracle'
*.nls_language='NORWEGIAN'
*.nls_territory='NORWAY'
*.open_cursors=300
*.optimizer_adaptive_plans=FALSE
*.optimizer_dynamic_sampling=0
*.optimizer_mode='ALL_ROWS'
*.pga_aggregate_target=7222M
*.processes=1500
*.remote_login_passwordfile='EXCLUSIVE'
*.sga_target=21696M
*.shared_pool_size=2624M
*.streams_pool_size=256M
*.undo_tablespace='UNDOTBS1'
*.diagnostic_dest=/u01/oracle

4. I prefer using an spfile over a pfile. Therefore I take the time here to create an spfile:
sqlplus / as sysdba
startup nomount pfile=inittest1.ora
create spfile from pfile;

5. Startup the auxiliary instance in nomount-mode using the spfile:
shutdown abort
startup nomount

6. Start the duplication process:
rman auxiliary /
duplicate database to test1
backup location '/u05/fra/bup';

RMAN went through the normal restore, name switching and recovery phases. Finally, the database was opened with the resetlog option.