ODBC 32bits for Windows 64bits

Windows-On-Windows 64-bit (WOW64) enables you to run 32bits applications in 64bits OS.

You will see there is another powershell, another registry, another ODBC tool, another Oracle client.


%SYSTEMROOT%\syswow64\WindowsPowerShell\v1.0\powershell.exe

First, we run powershell(x86)


Get-itemproperty HKLM:\SOFTWARE\ORACLE\*| Select-Object ORACLE_HOME,ORACLE_HOME_NAME

ORACLE_HOME                                                 ORACLE_HOME_NAME
-----------                                                 ----------------
C:\oracle\product\11.2.0\client_32                          client32bit_11203

Only the Oracle 32bit client is displayed

cmd /c "%SYSTEMROOT%\syswow64\odbcconf.exe /a {configdsn ""Oracle in client32bit_11203"" ""DSN=helloworld32|SERVER=DB01""}"

We registered ODBC with a wow64 configurator (odbcconf) or assistant (odbcad32).


$conn = New-Object Data.Odbc.OdbcConnection
$conn.ConnectionString= "dsn=helloworld32;uid=scott;pwd=tiger;"
$conn.open()
(new-Object Data.Odbc.OdbcCommand("select 'Hello World' from dual",$conn)).ExecuteScalar()
$conn.close()

For the 64 bits version, it boils down to the same as odbc 32 bit on 32 bit os


%SYSTEMROOT%\system32\WindowsPowerShell\v1.0\powershell.exe

Get-itemproperty HKLM:\SOFTWARE\ORACLE\*| Select-Object ORACLE_HOME,ORACLE_HOME_NAME

ORACLE_HOME                             ORACLE_HOME_NAME
-----------                             ----------------
C:\oracle\product\11.2.0\client_64      client64bit_11203

cmd /c "%SYSTEMROOT%\system32\odbcconf.exe /a {configdsn ""Oracle in client64bit_11203"" ""DSN=helloworld64|SERVER=DB01""}"

$conn = New-Object Data.Odbc.OdbcConnection
$conn.ConnectionString= "dsn=helloworld64;uid=scott;pwd=tiger;"
$conn.open()
(new-Object Data.Odbc.OdbcCommand("select 'Hello World' from dual",$conn)).ExecuteScalar()
$conn.close()

Do not get confused by Windows32 and WOW64.

Windows32 is the default windows system directory with a bad name from upon a time where 32 meant something very big (compared to 16bit software); and WOW64 which is a special directory to run 32bits windows application on a 64bits os.

Wasted Space

Here’s a little quiz: If I take the average row length of the rows in a table, multiply by the number of rows, and convert the result to the equivalent number of blocks, how can the total volume of data in the table be greater than the total number of blocks below the table high water mark ? I’ve got three tables in a schema, and they’re all in the same (8KB block, 1M uniform extent, locally managed) tablespace, but here’s a query, with results, showing their space utilisation – notice that I gather schema stats immediately before running my query:


execute dbms_stats.gather_schema_stats(user, method_opt=>'for all columns size 1');

spool waste_space

select
        table_name,
        avg_row_len,
        num_rows,
        round(avg_row_len * num_rows / 8100)    used_space,
        blocks
from
        user_tables
order by
        table_name
;

TABLE_NAME           AVG_ROW_LEN   NUM_ROWS USED_SPACE     BLOCKS
-------------------- ----------- ---------- ---------- ----------
T1                            85       5000         52         63
T2                            85       5000         52         18
T3                           107       5000         66         32

The division by 8,100 is a fairly crude approximation to convert to from bytes to blocks – it allows for the basic block overhead, but doesn’t make any allowance for PCTFREE, ITL entries, etc. It’s generally good enough for a ball-park figure though. So one of my tables uses about 52 blocks’ worth of space, below a highwater mark of 63 blocks – but two of the tables are, apparently, using more space than has been made available !

There are probably several mechanisms that could produce these results – without using any elaborately contrived sequences of activity – all I did was “create as select” to come up with two very simple options which fell outside the boundaries of what the simple SQL diagnostic was able to handle. Any ideas ? I’ll be posting my examples later on today.


Man of Steel

I’ve just got back from watching Man of Steel at the cinema.

I went into this film with extremely low expectations. For people of my age, this is the third time round for this story, so I expected to be pretty bored from a plot perspective.

I’m going to split the film into three parts.

  1. The first part of the story concerned the birth of Kal-El and him getting sent to earth. I expected this to be really dull and a bit annoying because of Russell Crowe’s presence. Actually is turned out to be completely brilliant. If the whole film had been similar to this first section it would probably have been the greatest Sci-Fi movie I had ever seen. If all you do is go in, watch this first sequence then leave, you will have had your money’s worth, especially since it was only £3 to get in on Tuesday night. :)
  2. The second part involved Kal-El growing up and becoming Superman. I also expected this to be a little dull, but actually is was really neat. They approached this part of the story in a different way to the previous films. It worked really well and I actually felt myself starting to care about the lead character.
  3. The third part of the film was just disaster porn. I found it really dull and generic. In parts it felt like a rip-off of the last fight scene in The Matrix Revolutions, mixed in with anything Michael Bay has ever done. I found myself hoping everyone would just hurry up and die so I could go home. Sometimes I find this stuff mildly amusing, but most of the time I just zone out and wonder what I am going to get to eat on the way home…

On my way out I was listening to a few conversations. One woman said, “The acting was terrible and I am so bored with seeing buildings get blown up!” I heard a group of guys talking in the car park and their conversation distilled down to, “He just didn’t do anything for the last half of the film!”

As it stands, I enjoyed it a lot more than I expected, but after a fantastic start it degenerated into mediocrity.

Cheers

Tim…


Man of Steel was first posted on June 19, 2013 at 12:04 am.
©2012 "The ORACLE-BASE Blog". Use of this feed is for personal non-commercial use only. If you are not reading this article in your feed reader, then the site is guilty of copyright infringement.

Oracle ODBC hello world with powershell

Demo :


cmd /c "odbcconf.exe /a {configdsn ""Oracle in OraClient11g_home1"" ""DSN=helloworld|SERVER=DB01""}"

Create a helloworld data source connecting to your DB01 tns alias in your OraClient11g_home1 Oracle Home.

It is easy to get the Oracle Home name with


Get-itemproperty HKLM:\SOFTWARE\ORACLE\*| Select-Object ORACLE_HOME,ORACLE_HOME_NAME

ORACLE_HOME                                       ORACLE_HOME_KEY
-----------                                       ---------------
C:\oracle\product\11.1.0\client_1                 OraClient11g_home1
C:\oracle\product\11.2.0\client_1                 OraClient11g_home2

Then we create the connection (as we did in ADO or ODP) :


$conn = New-Object Data.Odbc.OdbcConnection
$conn.ConnectionString= "dsn=helloworld;uid=scott;pwd=tiger;"
$conn.open()
(new-Object Data.Odbc.OdbcCommand("select 'Hello World' from dual",$conn)).ExecuteScalar()
$conn.close()

Delphix

If you’ve been keeping an eye on my Public Appearances page you’ll know that I am scheduled to go on line with Kyle Hailey for a second (more technical) discussion about Delphix and virtual databases on 19th June (tomorrow). If you haven’t registered, there’s still time to do so. It’s scheduled for 5:00 pm (BST), which makes it 9:00 am in San Francisco.

For an idea of the points we’ll cover, here’s a link with a draft agenda that Kyle Hailey has posted.

Update 1: Delphix have got 10 copies of Oracle Core to give away and they’ve decide to give one to every 10th registrant (until stocks run out) for the webinar.

Update 2: Over the last few days Kyle Hailey has been writing a short series comparing the commonest technologies currently available for Virtual Databases (or “Thin cloning”):


Running in the Background

Archives

We all have that list of features we just wish Oracle would adopt in their latest version: alterable redo log files, command history in SQL*Plus, DBA_OOPS package with various rollback options when you screw something up…

One of my biggest wishlist items is detachable sessions. In Oracle 10g we got the Datapump tool for exports and imports. One of its best features is the ability to detach from a running export or import and re-attach as needed. When you kick off an import (impdp) you can press CTRL+C and drop out to a client. From there, you can type ‘exit_client’ and detach completely, allowing you to go home, get a coffee, turn off your computer, or otherwise amuse yourself in whatever way seems best. For example:

bash-3.00$ expdp system/******** directory=dpexp full=y dumpfile=test.dmp

Export: Release 11.2.0.3.0 - Production on Mon Jun 17 07:27:50 2013

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Starting "SYSTEM"."SYS_EXPORT_FULL_02":  system/******** directory=dpexp full=y dumpfile=test.dmp 
Estimate in progress using BLOCKS method...
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 52.70 GB
^C
Export> exit_client

bash-3.00$

As you can see, pressing CTRL+C allowed me to exit the client back to a bash prompt. Since the task is owned by the job SYS_EXPORT_FULL_02, it stays running in the background. However, I can re-attach to the job and use continue_client to keep watching, or:

bash-3.00$ expdp system/******** attach
...
Export> kill_job
Are you sure you wish to stop this job ([yes]/no): yes

bash-3.00$

And there it is. But the important thing to note is that when I kick off an export or import with expdp and impdp, it always runs as a job. That means that your foreground process is only attaching to the background process running the job; this is what allows you to attach and detach at will.

At the same time, that is technically always the case. When you connect to the database in SQL*Plus and run a command, your SQL*Plus process is not the one performing the work; instead, the commands are run against the DB with your server process–LOCAL=NO owned by init for remote connections, or LOCAL=YES owned by your SQL*Plus process for non-remote connections. It would be very nice to be able to detach and re-attach to tasks run from any Oracle client.

That being said, it’s not possible. So here are some ways to run your work in the background that are useful for cases where you have a long running process and aren’t sure if you can wait around for it to complete.

NOHUP + Background

Most everyone knows NOHUP (No Hangup). It is a command run from the UNIX/Linux prompt in which you call your script, usually with a ‘&’ sign at the end signifying you want the work to occur in the background, like so:

steve@220883 backgrounder]$ nohup ./longrun.sh &
[1] 6446
[steve@220883 backgrounder]$ nohup: appending output to `nohup.out'

[steve@220883 backgrounder]$

You can still keep an eye on your job with the ‘jobs’ command, and even kill the job if need be:

[steve@220883 backgrounder]$ jobs
[1]+  Running                 nohup ./longrun.sh &
[steve@220883 backgrounder]$ kill %1
[1]+  Terminated              nohup ./longrun.sh
[steve@220883 backgrounder]$

Running a command with ‘nohup’ is a great way to kick something off in the background, allowing you to disconnect and do other things.

Disown

But what if you forgot to run the nohup command? You can still kick your program to the background with CTRL+Z and run it in the background; however, you still own it:

[steve@220883 backgrounder]$ ./longrun.sh
[1]+  Stopped                 ./longrun.sh
steve@220883 backgrounder]$ bg
[1]+ ./longrun.sh &

Notice that pressing CTRL+Z stopped the job, and typing ‘bg’ ran it in the background (signified by the ‘&’ sign). However, without NOHUP the command is still owned by my session. If I exit right now it will hang and the fate of my background job will be unclear.

To get around this problem, you can use the ‘disown’ command:

[steve@220883 backgrounder]$ disown
[steve@220883 backgrounder]$

Nothing special happens in the foreground. But in the background, your job is now owned by init (PID 1). You can feel free to exit and go about your business.

Special Note: There is a special place in Hell for those who CTRL+Z out of ‘vi’.

Screen

Shamelessly borrowed from 5 Quick and Dirty Linux Tips You May Not Know

This one was a godsend. Have you ever been working on a long running operation and you lost your connection to Oracle, or had to go home, or god knows what else? It is a horrible pain and one that really messes up a lot if it gets you at the wrong time. That should be a thing of the past if you use screen.

Screen is a multiplexed terminal, allowing you to spawn multiple terminals in a single terminal session. Opening a new ‘screen’ is simple; simply type the word screen at a Linux command line:

[skaram@server2 ~]$ echo HELLO!
HELLO!
[skaram@server2 ~]$ screen

Notice your terminal clears and you start at a new prompt. Now I will go ahead and make it wait for input.

[skaram@server2 ~]$ read -p "Press Enter"
Press Enter

If I press CTRL+A, then CTRL+D, my screen will detach, putting me back on the ‘parent’ terminal session:

[skaram@server2 ~]$ 
[skaram@server2 ~]$ 
[skaram@server2 ~]$ echo HELLO!
HELLO!
[skaram@server2 ~]$ screen
[detached]
[skaram@server2 ~]$

If I want to get back to the child screen, I can type screen -rx to re-attach. Or start a new terminal with a fresh ‘screen’ command. You can even detach from multiple terminals and connect to the one of your choosing:

[skaram@server2 ~]$ screen -list
There are screens on:
	10913.pts-2.server2	(Detached)
	10883.pts-2.server2	(Detached)
	9700.pts-2.server2	(Detached)
3 Sockets in /var/run/screen/S-skaram.

[skaram@server2 ~]$ screen -rx 10883.pts-2.server2

But wait, there’s more! If you and a buddy, coworker, consultant, engineer, parole officer, etc. want to share the same screen, you can login as the same user via SSH and then both type screen -rx. You will both join the multiplexed session (screen) and be able to type and see what each other is typing. It is awesome for following along, mentoring, etc.

DBMS_SCHEDULER

While Oracle Scheduler is an outstanding tool for running tasks in the future or on a schedule, it can also be used to run tasks right away. The best part is that since it is native to Oracle, it can run a PL/SQL block in addition to a shell script. That means if all you need to do is gather some stats or run a big update, you don’t need to make a shell script wrapper and nohup it. For example:

begin
  dbms_scheduler.create_job('stats_gather_test_uno',
    job_type=>'plsql_block',
    job_action=>'begin
dbms_stats.gather_schema_stats(ownname => ''SCOTT'');
end;',
    enabled=>true);
end;
/

That command will create a job called STATS_GATHER_TEST_UNO that gathers stats on the SCOTT schema right away in the background. You can use DBA_SCHEDULER_RUNNING_JOBS and other views to get more info on its progress. If you want to run a shell script, then job_type should be set to ‘EXECUTABLE’ and job_action will be the script itself.

Note: There are two single quotes around SCOTT in that example, as we are using single quotes inside of single quotes. If you need to run something like ‘execute immediate’ you will have to use even more quotes, for example:

'begin
execute immediate ''something with ''''quotes'''''';
end;'

Ouch. You can get around this problem with q-quotes, introduced in 10gR1.

Conclusion

These options are a far cry from a real backgrounding capability for Oracle sessions. It would be great if we could do something like DBMS_SESS.DETACH(sid=>123, jobname=>’way_too_long’) then re-attach as needed. But until that day, there are some viable options as long as you show a little forethought.

You may have noticed that ‘cron’ didn’t make the cut. We all know and love the cron tool for scheduling something that we don’t want running in our terminals; however, it’s already a very commonly blogged-about tool and somewhat inadequate for our “just run it once in the background please, thanks” requirements.

The post Running in the Background appeared first on Steve Karam :: The Oracle Alchemist.

The Story of Two Boats and Why Justin Bieber Owes Me £120* (Part 1)

(* Inspired by Cary Millsap, although the story isn't as elegant as one of his ...)

It's 3 weeks, 4 weeks, 2 months since I disembarked from my second boat cruise in a week but it already seems longer. Perhaps it's because my client had thoughtfully waited until I got back into the office for the next Production release of the application I've been working on. That went well, but the subsequent infrastructure performance issues were the latest part of a 5 week long haze, and then it was time to handover to my replacement and make a start on a new role. It's been an extremely busy few months :-((

Sunday
My almost traditional pre-conference illness (maybe it's because these things are in Autumn and Spring or maybe it's pre-conference stress - who knows?) and the last frantic bits of work for the release on the horizon meant that I had very little time to work on my two new presentations so when I set off for Helsinki on Sunday, it was with laptop powered up and ready to go. Fortunately I had managed at least half of one of the presentations before I got ill. What I didn't have yet was a hotel room in Oslo for the Wednesday night because when I got around to checking on Friday, there was literally nothing. I was utterly baffled (really, how often is there *nothing* except for a hostel?) but decided I could sort that out later ....

The time difference stole another two hours from me (these things seem more signifcant when your back's against the wall) but it was a pleasant, uneventful trip and a quiet night in the hotel prepared me for setting sail on Silja Serenade the next morning.

Monday
Having never spent any time in Helsinki, I decided I might as well walk to the boat and Helsinki looks like a beautiful city that I'll be going back to at some point. Once on board and unpacked in my beautiful sea-view room, I realised that there was a stowaway on board! He has a habit of getting lost ...

Gerald the Stowaway
Gerald the Stowaway


Tom Kyte and Bryn Llewellyn had the good grace to give keynote presentations using slides that I'd seen several times by now which gave me most of the first day to work on ...  (you get the picture). Never have I been so happy to presenting on the second day of a two-day conference ;-) But I was very keen to see Melanie Caffrey's 'Keeping It Simple in Database Application Development'. She'd been giving this presentation at Oak Table Sunday last year at the same time as my 8-bit presentation, so I'd missed out then.

Although I know Melanie through her being a regular at the annual UKOUG conference, I'm not sure that I've ever seen her present much before, but she was as engaging and smart as I expected as she went through some of the lessons learned in her role as a Senior Development Manager at Oracle, working on linux.oracle.com. I wish I could remember more of them now, but sometimes I'm so busy listening to the messenger that I can miss some of the message! LOL

I'd warned Melanie how weird it is to be presenting just as the boat sets sail because I remember it from my first time and, sure enough, stuff started clanging, the boat started humming and swaying ever so slightly - it's an experience everyone should have at least once ;-) She also learned just how polite and quiet Finnish people are when in a sober audience of more than a few people. Never try a rhetorical question to the audience - you're likely to just get stoney silence back! However, they truly come into their own when you add beer or have a more private conversation - somewhat like most Scots I know!

After that, Heli Helskyaho (@HeliFromFinland) gave the OUGF 25th Anniversary Speech, flipping effortlessly between Finnish and English which is about as impressive as it gets to my ears. Although I have to say, it seems that this 25th Anniversay thing is a bit debatable. Is it 25 or 26 or .... I think someone forgot to start counting!

One of the most enjoyable aspects of a boat conference is the food, and we had one of the first of many great meals, all with plenty of wine thrown in and I was lucky enough to have Melanie, Debra Lilley and Alex Nuijten for company so it was a great way to wrap the day up, particularly as I had almost finished my slides ;-)

Tuesday

Tuesday was a day of worrying about and deliverying two presentations and feeling not a little unwell. It wasn't so much seasickness as the continuation of the long-running cold that I'd shared with lots of other people, so I was a little tense when I started presenting "10053 Trace Files - Mostly Harmless", particularly as Tom Kyte and Melanie were in the audience, knowing Tom's encyclopedic knowledge of Oracle! But it seemed to go reasonably well for the first time giving this presentation, but with some areas that I can improve, particularly some more useful examples.

Then there was time to actually eat some lunch (which is an achievement for me on presentation day) before I moved on to "Fast ETL Processes using Native Oracle Features". But im the lead-up, I felt really sick and I was concerned I might perform the impressive but disgusting trick of being physically sick mid-presentation! I mentioned the possibility on Twitter, so Alex made sure he turned up with his phone at the ready so he could tweet the evidence! Fortunately I got through it ok. Again, it probably needs a little more polish but the small room was absolutely packed and it went well enough. What I needed most was to lie down and rest, which meant that I missed Alex's Analytic Functions presentation again (something I have a long personal history of doing ...)

It was worth it though, as I was in a much better state when I made it back to the conference area just in time for Heli's wrap-up session. As well as having a prize-giving raffle from the numbers on peoples conference badges during which some really inconsiderate delegates walked off with all of the Cuddly Toys ;-), Heli tossed balls around the room to elicit feedback from whichever attendee caught the balls and she asked what was their best thing about the conference. At first I thought it might be embarassing and took a while to get going (Scots and Finns have a lot in common!) but it actually worked really well because I don't think anyone would have volunteered otherwise ;-) Maybe the real reason I liked the idea was when one of the delegates said that his favourite thing was my presentations, which was brilliant to hear. I always speak at a lot of conferences with great speaker line-ups, so I've never actually been told that in the past! Others who I won't mention are used to it on a regular basis *cough* Jonathan, Tom, Cary ... *cough*

Which put me in a great mood for the final dinner which was delicious, as all the catering was. Did I mention? But if you could have seen Bryn, Tom and Melanie *piling* the starters on to their plates, you would know I wasn't exaggerating. In fact, there were so many interesting and tasty options (particularly for those with wider tastes than me) that a lot of people just about managed a bunch of starters and then a few puddings. Between the company and the food, I didn't think it could get much better, but then I was voted Speaker of the Day. Again, that's something that never happened to me and I probably shouldn't care, but I'm afraid I did, particularly when I considered the other speakers on the agenda. Of course, I cheated a bit by doing two presentations on one day but, hey, I'll take what I can get ;-) Heli presented me with a Moomin mug and some whisky

Moomin Mug
Moomin Mug

In the interests of actually submitting a blog post, I'll leave it there for now. Justin Bieber? He can wait!

Hints again

A recent posting on OTN came up with a potentially interesting problem – it started roughly like this:

I have two queries like this:

select * from emp where dept_id=10 and emp_id=15;
select * from emp where dept_id=10 and emp_id=16;

When I run them separately I get the execution plan I want, but when I run a union of the two the plans change.

This, of course, is extremely unlikely – even if we assume that the two queries are more complex than the text shown. On the other hand you might, after a little thought, come up with the idea that perhaps the optimizer had done something really clever like join factorization (moving a join that’s common to the two parts of the UNION from inside to outside the UNION), or maybe there’s some really new trick the optimizer had played because a UNION ultimately requires a SORT UNIQUE, and the optimizer had chosen a different path that returned the data from each part of the UNION in sorted order to decrease the cost of that final sort.

In fact it turned out to be a lot simpler than that. The query looked more like this:


select
	/*+
		index(@qb_view_a t1)
		index(@qb_view_b t1)
	*/
	*
from
	t2, qb_view
where
	t2.n1 = 10
and	qb_view.n2  = t2.n2
union
select
	/*+
		index(@qb_view_a t1)
		index(@qb_view_b t1)
	*/
	*
from
	t2, qb_view
where
	t2.n1 = 12                    -- the real code referenced an alternative column here.
and	qb_view.n2  = t2.n2
;

Of particular note is the fact that it’s a join, the join involves a view (guessing from the names in the FROM clause) and there are hints that reference query block names for query blocks that don’t exist – but perhaps are present inside the view. So what does the view look like.


create or replace view qb_view
as
select /*+ qb_name(qb_view_a) */ * from t1
union all
select /*+ qb_name(qb_view_b) */ * from t1
;

It’s a union all view – and the two query blocks named from the outside query are the two halves of the inner union.

Here’s an important thought – it’s quite easy to get Oracle to do what you want in a simple query (at least in the short term) by sticking in a few hints – especially if you create and reference query block names; but when you start compounding queries by combining bits of code that currently do what you want, you may find that Oracle introduces extra query blocks during transformation, and perhaps some of the query blocks you’ve referenced originally cease to exist, so the hints no longer apply.

Let’s look at the execution plan – including the ALIAS and OUTLINE sections – for the final query, and compare it with the execution plan for just one of the two pieces; starting with the single piece first:

explain plan for
select
	/*+
		index(@qb_view_a t1)
		index(@qb_view_b t1)
	*/
	*
from
	t2, qb_view
where
	t2.n1 = 10
and	qb_view.n2  = t2.n2
;

select * from table(dbms_xplan.display(null,null,'basic +outline +alias'));

--------------------------------------------------
| Id  | Operation                      | Name    |
--------------------------------------------------
|   0 | SELECT STATEMENT               |         |
|   1 |  HASH JOIN                     |         |
|   2 |   TABLE ACCESS FULL            | T2      |
|   3 |   VIEW                         | QB_VIEW |
|   4 |    UNION-ALL                   |         |
|   5 |     TABLE ACCESS BY INDEX ROWID| T1      |
|   6 |      INDEX FULL SCAN           | T1_I1   |
|   7 |     TABLE ACCESS BY INDEX ROWID| T1      |
|   8 |      INDEX FULL SCAN           | T1_I1   |
--------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1     / T2@SEL$1
   3 - SET$1     / QB_VIEW@SEL$1
   4 - SET$1
   5 - QB_VIEW_A / T1@QB_VIEW_A
   6 - QB_VIEW_A / T1@QB_VIEW_A
   7 - QB_VIEW_B / T1@QB_VIEW_B
   8 - QB_VIEW_B / T1@QB_VIEW_B

Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      INDEX(@"QB_VIEW_A" "T1"@"QB_VIEW_A" ("T1"."N1"))
      INDEX(@"QB_VIEW_B" "T1"@"QB_VIEW_B" ("T1"."N1"))
      USE_HASH(@"SEL$1" "QB_VIEW"@"SEL$1")
      LEADING(@"SEL$1" "T2"@"SEL$1" "QB_VIEW"@"SEL$1")
      NO_ACCESS(@"SEL$1" "QB_VIEW"@"SEL$1")
      FULL(@"SEL$1" "T2"@"SEL$1")
      OUTLINE(@"QB_VIEW_B")
      OUTLINE(@"QB_VIEW_A")
      OUTLINE_LEAF(@"SEL$1")
      OUTLINE_LEAF(@"SET$1")
      OUTLINE_LEAF(@"QB_VIEW_B")
      OUTLINE_LEAF(@"QB_VIEW_A")
      ALL_ROWS
      DB_VERSION('11.2.0.3')
      OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */

The plan shows us that we have used an index to access table t1 (with an index full scan) in both halves of the QB_VIEW’s union all; and the alias section shows us that we have a table t1 in a query block name qb_view_a, and the outline section shows that we have a hint applied in that query block that directs the optimizer to use an index on that table: INDEX(@”QB_VIEW_A” “T1″@”QB_VIEW_A” (“T1″.”N1″)); and we can see the same strategy appearing for a table t1 in query block qb_view_b. By the way, I checked the plan without the hints, and the optimizer chose to do full tablescans on t1 – so the hints were actually having an effect.

So what happens when we check the plan for the UNION of the two variants of the query:


------------------------------------------
| Id  | Operation              | Name    |
------------------------------------------
|   0 | SELECT STATEMENT       |         |
|   1 |  SORT UNIQUE           |         |
|   2 |   UNION-ALL            |         |
|   3 |    HASH JOIN           |         |
|   4 |     TABLE ACCESS FULL  | T2      |
|   5 |     VIEW               | QB_VIEW |
|   6 |      UNION-ALL         |         |
|   7 |       TABLE ACCESS FULL| T1      |
|   8 |       TABLE ACCESS FULL| T1      |
|   9 |    HASH JOIN           |         |
|  10 |     TABLE ACCESS FULL  | T2      |
|  11 |     VIEW               | QB_VIEW |
|  12 |      UNION-ALL         |         |
|  13 |       TABLE ACCESS FULL| T1      |
|  14 |       TABLE ACCESS FULL| T1      |
------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SET$1
   3 - SEL$1
   4 - SEL$1 / T2@SEL$1
   5 - SET$2 / QB_VIEW@SEL$1
   6 - SET$2
   7 - SEL$2 / T1@SEL$2
   8 - SEL$3 / T1@SEL$3
   9 - SEL$4
  10 - SEL$4 / T2@SEL$4
  11 - SET$3 / QB_VIEW@SEL$4
  12 - SET$3
  13 - SEL$5 / T1@SEL$5
  14 - SEL$6 / T1@SEL$6

Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      FULL(@"SEL$5" "T1"@"SEL$5")
      FULL(@"SEL$6" "T1"@"SEL$6")
      FULL(@"SEL$2" "T1"@"SEL$2")
      FULL(@"SEL$3" "T1"@"SEL$3")
      USE_HASH(@"SEL$1" "QB_VIEW"@"SEL$1")
      LEADING(@"SEL$1" "T2"@"SEL$1" "QB_VIEW"@"SEL$1")
      NO_ACCESS(@"SEL$1" "QB_VIEW"@"SEL$1")
      FULL(@"SEL$1" "T2"@"SEL$1")
      USE_HASH(@"SEL$4" "QB_VIEW"@"SEL$4")
      LEADING(@"SEL$4" "T2"@"SEL$4" "QB_VIEW"@"SEL$4")
      NO_ACCESS(@"SEL$4" "QB_VIEW"@"SEL$4")
      FULL(@"SEL$4" "T2"@"SEL$4")
      OUTLINE_LEAF(@"SET$1")
      OUTLINE_LEAF(@"SEL$4")
      OUTLINE_LEAF(@"SET$3")
      OUTLINE_LEAF(@"SEL$6")
      OUTLINE_LEAF(@"SEL$5")
      OUTLINE_LEAF(@"SEL$1")
      OUTLINE_LEAF(@"SET$2")
      OUTLINE_LEAF(@"SEL$3")
      OUTLINE_LEAF(@"SEL$2")
      ALL_ROWS
      DB_VERSION('11.2.0.3')
      OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */

The plan is completely different. We’ve lost the index full scans and we’ve reverted to the tablescans that we would have got from an unhinted query. When you look at the outline you can see why – the query blocks qb_view_a and qb_view_b have disappeared – so the hints are no longer valid. As you can see we now have four occurrences of table t1, but as the alias section shows they come from query blocks sel$2, sel$3, sel$5 and sel$6).

Is this a bug ? I don’t think so. When the optimizer produces the outline information (which can be stored as an SQL Baseline in 11g) it’s producing a set of hints that will be applied at the outermost query block, with hints that point, as necessary, to inner query blocks; this means you can’t use the same query block name twice in a query or the optimizer wouldn’t be able to identify which query block a hint was supposed to apply to. So Oracle has eliminated duplicate query block names and replaced them with the standard internally generated ones – the user’s hints no longer apply.

Footnote: Checking the clock – it took me about 15 minutes to create a simplified model based on the information available on OTN: it’s taken me 75 minutes to describe what I did and what I learned as a result. With a little practice you can get very good at creating models that help you to identify and solve problems very quickly.


Webinar questions

Following the webinars about 11g stats that I presented on Monday John Goodhue emailed me a few questions that had come through the chat line while I was speaking, but hadn’t appeared on my screen. He’s emailed them to me, so here are the questions and answers.

1. I’d like to know what parameter to use for faster results on dbms_stats.gather_dictionary_stats

I don’t think I’ve come across any complaints about gathering dictionary stats being particularly slow, and you haven’t mentioned a specific version so I can’t make any comment about possible bugs. If you can, you first need to find out where most of the time is going, so tracing the call would be a good idea to start with. If you can’t trace it you could look at the “standard” types of problem such as: AWR history tables getting very large, optimizer stats history tables (particularly the histogram one) getting very large; synopsis tables getting very large. Histograms can be very expensive to collect, especially on partitioned tables, and many of the 11g dictionary tables are partitioned – so you might want to changed the method_opt for the collection to “for all columns size 1″. It is possible to get Oracle to dispatch multiple jobs to collect stats (the global preference CONCURRENT) if the issue is elapsed time rather than workload – check dbms_stats.set_global_prefs(), parameter CONCURRENT.

2.  Do incremental statistics also work with sub-partitions?

No. Even when you have a composite partitioned table, Oracle only uses the approximate NDV method to create synopses at the partition level. Given that a synopsis is up to 16,384 rows for each column for each partition, the volume of synopsis information that could be generated by extending the mechanism down to the subpartition level could be catastrophic.

3. I have always been benefiting from block_sample=>TRUE .. I do not see you using it – is there any reason to not use it {while having in mind of course, that the accuracy may suffer a bit, with heap tables, at the cost of faster gathered stats}

The only good reason not to use it is the reason you’ve given - there are patterns of skewed data that could make a block sample even less accurate on the number of distinct values than a row sample. The particular benefit of the block sample (for “safe” distributions) is that even a small percentage row sample may require Oracle to read every block of the table as it picks pseudo-random rows – perhaps even doing “db file scattered read” requests to scan the whole table and discarding lots of blocks (one of reasons behind statistic “prefetched blocks aged out before use”) because it didn’t need rows from them. With the 11g approximate NDV, of course, the question is moot since the new mechanism examines every row in the table.

4. is this #rows * #columns sorting for count distinct as well happening for any distinct? (as SELECT DISTINCT is generally a group by every selected column)

You’re referring to my comment about “select count(distinct n1) , count(distinct n2). count(distinct n3) from table;” seems to turn each row into N rows (where N = 3 in this case) of (column id, column value) and then sort the entire set in one operation rather than N concurrent operations. I don’t think you can generalise this, you’d have to check each SQL pattern separately – initially just checking the execution plan. For example, something like ‘select distinct n1, n2, n3 from table” is finding distinct combinations, and it will do a hash unique on the number of (non-null) rows in the table irrespective of the number of columns selected.

5. what is your view on using table preferences to explicitly specified required histograms?

I made a brief statement about collecting histograms for anything but your explicitly chosen columns; following up with the suggestion that your default action should be “for all columns size 1″ followed by explicit code to generated histograms for specific tables. I would be perfectly happy to set the global prefs to “for all columns size 1″ and then set table prefs to something specific for the columns I really wanted; the only reason that I don’t put this forward as a standard practice is that it does conceal what’s going on, and it’s easy to forget that some tables have preferences set. If your organisation is really good about documentation of systems then it makes perfect sense.  Having said that, though, don’t forget that I also pointed out that sometimes you may still need to create “fake” histograms to get the best possible results.

6.  (paraphrased)  I think Jonathan should have acknowledged that his presentation was based on the work of Amit Poddar.

I agree. This was an error of omission that I shouldn’t committed. It has been several years since Amit first described how Oracle uses a hash table for the approximate NDV and keeps halving it when the number of hash values reaches 16,384. When he closed his website he allowed me to publish the original presentation and white paper (64 pages) on my blog so I really should have remembered that the algorithm is still so little-known that he still merited attribution. As far as I am aware his work is still the most thorough and detailed description of how the algorithm works, although Oracle has extended it in 12c (as explained in the presentation I did about histograms for OOW 2012).


how to rename datafiles on standby

If you rename a file with alter database on primary, it will not occur on standby. If you want to rename it on standby to, you need to do it manually

  1. cancel the recovery (set dg_broker_start to false and restart db in mount status if you use dataguard)
  2. set the standby file management to manual alter system set standby_file_management=manual
  3. move the file with OS commands as you did on the primary
  4. rename the file with alter database rename file ‘old’ to ‘new’
  5. reenable automatic standby file management and dg broker and restart the recovery (start db in mount status if you use dataguard)