Oracle Drop and Recreate Existing Materialized View

Couldn’t find a script online that appropriately dropped and recreated an MV. We had an issue using impdp on 11g R1 where the MVs were imported under the right schema user, but for some reason, we couldn’t refresh the MV. The solution was to drop and recreate the MVs.


-- Drop and recreate MV for the current schema owner by Theo Chakkapark (http://suteki.nu)
DECLARE
	v_sql varchar2(32767);
 	TYPE mv_tables IS TABLE OF dba_mviews%rowtype INDEX BY PLS_INTEGER;

	mvrows mv_tables;
BEGIN

    	SELECT * BULK COLLECT INTO mvrows FROM user_mviews;
	
	IF(mvrows.count  0) THEN
	  FOR i IN mvrows.FIRST .. mvrows.LAST
	  LOOP
      v_sql := 'DROP MATERIALIZED VIEW ' || mvrows(i).owner || '.' || mvrows(i).mview_name;
      execute immediate (v_sql);
      dbms_output.put_line(v_sql);
      v_sql := 'CREATE MATERIALIZED VIEW ' || mvrows(i).owner || '.' || mvrows(i).mview_name || ' 
BUILD IMMEDIATE REFRESH COMPLETE AS ' || mvrows(i).query;
      execute immediate (v_sql);	  
      dbms_output.put_line(v_sql);		
      END LOOP;
	END IF;

END;
/


Native Compile for PL/SQL on Windows Oracle 10g R2 x64

I couldn’t find any definitive guides on how to compile PL/SQL natively on Windows, so after reading lots of documentation, I finally managed to figure it out. You will need the following:

- Visual Studio .NET 2003. .NET 2005 does not work (at least from what I’ve read), and if you use the x64 version of Oracle, MinGW will not work either. It’s gotta be Visual Studio .NET 2003 with the C++ component installed (ALL components, including the unchecked win platform one)
- Microsoft SDK for Windows. Our boxes are Windows Server 2003 R2, so I downloaded the corresponding SDK for it.

- Important: Make sure when you install the Microsoft SDK, you install it into a directory called ‘C:Program FilesMicrosoft Platform SDK’. The default will be something like ‘C:Program FilesMicrosoft Platform SDK for Windows Sever 2003 R2′; you do not want this! Oracle will look specifically for the directory named ‘C:Program FilesMicrosoft Platform SDK’.

- Important: Make sure you have enough RAM to increase your SGA and PGA memory size. I’ve had cases where I’ve used native compilation only to find that the native version runs slower than the interpreted complation! Or worse, your programs will just crash.

Install both, and open up a sqlplus session (probably under the ‘system’ user), and run the following:


   alter system set plsql_native_library_dir='C:oracledb10gnative' scope=both;
   alter system set plsql_native_library_subdir_count=5 scope=both;
   alter system set plsql_code_type='NATIVE';

- plsql_native_library_dir should be pointing to a directory you specify. I created a ‘native’ directory in my Oracle installation and pointed it there. All the compiled PL/SQL will go into that directory.
- plsql_native_library_subdir_count determines how many directories to use when generating the compiled objects. The reason why this exists is if you have a large amount of procedures / packages to be compiled (large as in > 10,000), there will be I/O issues if you store all the compiled items in one directory. If you specify the count to be more than one, then each compiled item will be put in a subdirectory, round-robin style.
- Since plsql_native_library_subdir_count=5 in the example above, you need to manually create 5 directories in the ‘native’ directory created, called dn (where n is a number starting with 0 to subdir_count) ie:

C:Oracledb10gnatived0
C:Oracledb10gnatived1

C:Oracledb10gnatived4

At this point, try compiling your PL/SQL packages or procedures. You can check your subdirectories to see that things are being compiled.

Congrats! You should now have a (slightly) speedier Oracle system. Unfortunately, there are no speed improvements using native compilation for SQL statements. Instead, you need to do your standard optimization techniques for your schemas, such as creating proper indexes, partitions, etc. If you use heavy algorithms PL/SQL, native compilation will benefit this most.

And, if in the event you want to switch out of native compilation, use the following:

ALTER SYSTEM SET plsql_compiler_flags = 'INTERPRETED';
ALTER SYSTEM set plsql_code_type='INTERPRETED';

For more information, see the Oracle PL/SQL NComp document.

Do you use analytic functions?

I’m currently programming a who’s online for leads at work, and was having lots of difficulty in getting the results I needed to properly display the most recent activity.

We have three tables that need to be looked up due to the way the schema was designed. The primary table is a user_log table that stores all actions that a user performed along with a timestamp. However, this log table does not include any user information aside from a user_id. So, to get the information about a user, we need to join that user_id to the user_id in a users table.

But, here’s where it gets really messed up – the users table doesn’t contain the information we need. Instead, it has a contact_id field in the users table, which must be then joined to a contacts table.

So it goes like this:

.. WHERE user_log.user_id = users.user_id AND users.contact_id = contacts.contact_id

Yeah, three tables just to get contact information.

I was finding that the standard SQL Distinct/Group By keywords were proving ineffective in getting the results I needed, which would be to just give me one user_id and the most recent created timestamp in the user_log table. Instead, I was getting multiple rows for the same user_id.

Fortunately, I found out about analytic functions in Oracle, which helped me isolate out unique user_ids and get the latest timestamp for that user by using ROW_NUMBER( ) where the sequence_number = 1 (giving me the topmost result for each user_id).

Unfortunately, I also learned that mySQL does not have analytic functions built in, but it can be emulated in native SQL (Part 1) (Part 2).

The best explanation I can give about the common aggregation functions that most people tend to use like SUM(), AVG(), etc vs analytic functions is:

Assume you have a set of rows.

The aggregation function will take that set of rows and output only one row with the results, while an analytic function will output that set of rows with the results appended.

The analytic functions do not have to work this way, but it’s useful for performing ranking analysis or sorting rows into individual sets of results.

How to stop or kill data pump jobs in Oracle the CORRECT way

In my never ending frustrations with using Oracle (seriously, I loathe Oracle above all else), I could not find an absolute answer on how to stop or kill or delete data pump jobs being executed. I found the answer via Metalink, and I’m going to share it because I feel these answers should be easily accessible. It’s a two step process.

1. Get the list of datapump jobs:

SET lines 200
COL owner_name FORMAT a10;
COL job_name FORMAT a20
COL state FORMAT a11
COL operation LIKE state
COL job_mode LIKE state

-- locate Data Pump jobs:

SELECT owner_name, job_name, operation, job_mode,
state, attached_sessions
FROM dba_datapump_jobs
WHERE job_name NOT LIKE 'BIN$%'
ORDER BY 1,2;

The output might look something like this:

OWNER_NAME JOB_NAME             OPERATION   JOB_MODE    STATE       ATTACHED_SESSIONS
---------- -------------------- ----------- ----------- ----------- -----------------
SCHEMA_USER SYS_IMPORT_SCHEMA_01 IMPORT      SCHEMA      EXECUTING                   1

There are two things needed to perform the kill:
1. OWNER_NAME (Which is SCHEMA_USER)
2. JOB_NAME (Which is SYS_IMPORT_SCHEMA_01)

With that information, we can now stop and kill the job:

SET serveroutput on
SET lines 100
DECLARE
   h1 NUMBER;
BEGIN
  -- Format: DBMS_DATAPUMP.ATTACH('[job_name]','[owner_name]');
   h1 := DBMS_DATAPUMP.ATTACH('SYS_IMPORT_SCHEMA_01','SCHEMA_USER');
   DBMS_DATAPUMP.STOP_JOB (h1,1,0);
END;
/

Check that the job has stopped:

SQL> SET lines 200
SQL> COL owner_name FORMAT a10;
SQL> COL job_name FORMAT a20
SQL> COL state FORMAT a11 
SQL> COL operation LIKE state
SQL> COL job_mode LIKE state
SQL> 
SQL> -- locate Data Pump jobs:
SQL> 
SQL> SELECT owner_name, job_name, operation, job_mode,
  2  state, attached_sessions
  3  FROM dba_datapump_jobs
  4  WHERE job_name NOT LIKE 'BIN$%'
  5  ORDER BY 1,2;

no rows selected

Convert Oracle Date to UNIX Time

Oracle unfortunately does not have a UNIX conversion function. However, you can do it mathematically in SQL. I’ve written a function in PHP that will convert a date field to a UNIX timestamp in Oracle.

/**
* Inserts the SQL necessary to format a date to a UNIX time in Oracle.
* @param string Field name to convert.
* @param string (Optional) Field Alias
* @return string The SQL necessary to convert to UNIX time.
*/
function toUnixTime($field, $fieldAlias=null){
if($fieldAlias== null){
$fieldAlias= $field;
}

return "(ROUND((".$field." - to_date('01-01-1970', 'DD-MM-YYYY')) * (86400) + (28800)) ".$fieldAlias;
}

Usage:

$sql = “SELECT “.toUnixTime(“created”).” FROM users”;

or

//CreatedOn alias
$sql = “SELECT “.toUnixTime(“created”, “createdOn”).” FROM users”;

Oracle, Propel 1.3 and Dates

It took me awhile to figure out how to parse an Oracle date type with Propel 1.3. The easiest way to do it would be this:

define(“DATE_FORMAT”, ‘M j, Y g:i a’);

< ?=date(DATE_FORMAT, $event->getStartDate(null)->format(“U”));?>

Assume there is an Event that stores the date and time of when the event begins. The code above basically translates the Oracle date to UNIX time, where you can then use the PHP date function to parse it out.

CLOB and PDO, specifically Oracle

So, my new job has me working with Oracle which is in my book, a way different beast compared to mySQL. Everything I know about databases from mySQL basically had to be re-tooled for Oracle.

I spent around 3 hours trying to figure out how to work in a set of prepared statements (one of which returned a value) in PHP PDO, and thought I might share how I did this.

Okay, so I have one stored procedure and one function.

PROCEDURE RETURN_CLOB(:CLOB_ID, :CLOB_OUT IN CLOB) — All this does was search a table with key/ CLOB value pairs. It basically selected a row with the :CLOB_ID, and :CLOB_OUT returned the CLOB value.

FUNCTION CONVERT_CLOB_TO_VARCHAR2(:CLOB_OUT IN CLOB) RETURN VARCHAR2 — this was a function definition that took in the :CLOB_OUT from RETURN_CLOB and spit it out as a VARCHAR2

Note that I did not write these two procedures, it’s something out of a package I used at work, but was having trouble trying to get them to work in PDO.

I’m using PHP Propel 1.3, but you can easily rewrite this in standard PDO calls, since the $con object is actually a combination of the PDO class and PDO Statement class.
Here’s the code:

$con = Propel::getConnection(‘…’); //All this does is just use PDO to create a connection
/*

The call below is the same as PDOStatement::prepare(). I’ve condensed the following into one line:

DECLARE

CLOB_Output CLOB;

BEGIN

PackageName.RETURN_CLOB( :Clob_Id, CLOB_Output );

:Str := PackageName.CONVERT_CLOB_TO_VARCHAR2( CLOB_Output );

END;

//So the declare part is to define an internal variable in Oracle, which I’ve named CLOB_Output. It is of type CLOB. The Begin … End block processes the statements in one container.

1. Return_CLOB returns the CLOB data in CLOB_Output

2. Call CONVERT_BLOB_TO_VARCHAR2 with CLOB_Output

3. The converted CLOB is returned into :Str
*/
$stmt = $con->prepare(“DECLARE CLOB_Output CLOB; BEGIN PackageName.RETURN_CLOB( :Clob_Id, CLOB_Output ); :Str := PackageName.CONVERT_CLOB_TO_VARCHAR2( CLOB_Output ); END;”);

$m = “”;

$stmt->bindValue(‘:Clob_Id’, ‘Clob_Id_goes_here’, PDO::PARAM_INT);

$stmt->bindParam(‘:Str’, $m, PDO::PARAM_STR, 32767); //replace 32767 with the size of the VARCHAR2 that you are returning from CONVERT_BLOB_TO_VARCHAR2

$stmt->execute();
//$m now contains the CLOB as a VARCHAR2!
Phew! Anyways, I’m going to say that Propel 1.3 isn’t ready for Oracle yet, and neither is PHP_PDO for Oracle. There’s too many hassles and tricks I had to use to do the things I needed in Oracle, and I’m going to recommend people continue to use the OCI8 extension.