Monday, September 19, 2005

MySQL to an Oracle DBA, Part I

As you may remember, I am at MySQL training this week. The first day was introductory material which included installing MySQL and query basics. Because we didn’t breeze through the Query portion, it gave me plenty of time to experiment.


SQL> select count(*) from country;

COUNT(*)
----------
239


mysql> select count(*) from country;
+----------+
| count(*) |
+----------+
| 239 |
+----------+
1 row in set (0.00 sec)


OK, that’s cool, that’s what I expected. Let’s do a simple aggregate function:


SQL> select continent, sum(population) TotalPop
2 from country
3 group by continent
4 order by continent
5 ;

CONTINENT TOTALPOP
------------------------------ ----------
Africa 784475000
Antarctica 0
Asia 3705025700
Europe 730074600
North America 482993000
Oceania 30401150
South America 345780000

7 rows selected.


OK, that’s what we expected. Let’s try the same query in MySQL:


mysql> select continent, sum(population) TotalPop
-> from country
-> group by continent
-> order by continent
-> ;
+---------------+------------+
| continent | TotalPop |
+---------------+------------+
| Asia | 3705025700 |
| Europe | 730074600 |
| North America | 482993000 |
| Africa | 784475000 |
| Oceania | 30401150 |
| Antarctica | 0 |
| South America | 345780000 |
+---------------+------------+
7 rows in set (0.00 sec)


Hmm, that’s interesting. Maybe it has something to do with default sorting order or something. I’ll have to ask Tobias (my Instructor) about that in the morning. Let’s try sorting by a number field:


SQL> select continent, sum(population) totalpop
2 from country
3 group by continent
4 order by totalpop desc
5 ;

CONTINENT TOTALPOP
------------------------------ ----------
Asia 3705025700
Africa 784475000
Europe 730074600
North America 482993000
South America 345780000
Oceania 30401150
Antarctica 0

7 rows selected.

mysql> select continent, sum(population) totalpop
-> from country
-> group by continent
-> order by totalpop desc
-> ;
+---------------+------------+
| continent | totalpop |
+---------------+------------+
| Asia | 3705025700 |
| Africa | 784475000 |
| Europe | 730074600 |
| North America | 482993000 |
| South America | 345780000 |
| Oceania | 30401150 |
| Antarctica | 0 |
+---------------+------------+
7 rows in set (0.02 sec)


Yeah, that’s we expected. Let’s try another query:

mysql> select continent, population totalpop
-> from country
-> group by continent
-> order by totalpop desc
-> ;
+---------------+----------+
| continent | totalpop |
+---------------+----------+
| South America | 37032000 |
| Africa | 31471000 |
| Asia | 22720000 |
| Europe | 15864000 |
| North America | 217000 |
| Oceania | 68000 |
| Antarctica | 0 |
+---------------+----------+
7 rows in set (0.00 sec)


Hmm, what’s weird about this query (assuming the data is correct)? Let’s try it in Oracle:

SQL> select continent, population totalpop
2 from country
3 group by continent
4 order by totalpop desc
5 ;
select continent, population totalpop
*
ERROR at line 1:
ORA-00979: not a GROUP BY expression


That’s right, our old friend didn’t even run the query for us.

Another interesting tidbit was with the DISTINCT Operator. In Oracle, the optimizer may determine DISTINCT values by sorting, whereas MySQL can use one of two algorithms. For example:

SQL> select distinct continent from country;

CONTINENT
------------------------------
Africa
Antarctica
Asia
Europe
North America
Oceania
South America

7 rows selected.


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=6 Card=239 Bytes=7
648)

1 0 SORT (UNIQUE) (Cost=6 Card=239 Bytes=7648)
2 1 TABLE ACCESS (FULL) OF 'COUNTRY' (TABLE) (Cost=5 Card=23
9 Bytes=7648)

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
16 consistent gets
0 physical reads
0 redo size
665 bytes sent via SQL*Net to client
507 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
7 rows processed

mysql> select distinct continent from country;
+---------------+
| continent |
+---------------+
| Asia |
| Europe |
| North America |
| Africa |
| Oceania |
| South America |
| Antarctica |
+---------------+
7 rows in set (0.00 sec)


BTW, another participant in the class suggested that Oracle always sends back the results of DISTINCT in sorted order. I didn’t think so, but wasn’t able to disprove it. Maybe I learned something new about Oracle today.

Edit: 09/19/2005 23:33 -
Tom correctly points out in the comments that Oracle doesn't always use a sort to determine duplicates. If I had that looked on metalink at doc 655027.999 or 249919.999 I would have confirmed my suspicians about always using ORDER BY. Bang head on desk, Bang head on desk, ...

Saturday, September 17, 2005

We're doing MySQL

My manager called me into his office the other day and explained how we were going to put our first MySQL database up. It's a relatively simple app with 99% read activity and some batch loads in the night. This wasn't a total surprise as I did some investigation into MySQL about a year ago and determined that for the right types of application it would be adequate.

We've got some of the basics up in development already, but I feel like I'm a newbie all over again. When I can't figure something out, I say "That's how it works in Oracle" and need to step outside the box to figure it out.

This week is training in NYC. Five days of the basics will hopefully get me up to speed where I can be relatively proficient. Expect some blog entries next week about the differences between MySQL and Oracle.

Thursday, September 15, 2005

Top N

A Frequently Asked Question on many Oracle related forums is “How can I get the first/last N rows in my table?” Sometimes that’s not an easy question to answer. I always ask the poster what they mean by “first”. Some people mean the order the records were inserted, some people mean the top-N rows. I’ve found it’s always easier to get the poster to explain what they are trying to do instead of guessing.

First/Last – in an ordered set
This one is easy. You take advantage of inline views and Oracle’s pseudocolumn ROWNUM. People usually get tripped up when they use ROWNUM because while ROWNUM is relevant in all queries, in this type of query it only makes sense when coupled with an ORDER BY.

For example:

SQL> select * from xyz;

X Y Z
---------- ---------- -------------------
127 1 09/16/2005 14:46:50
148 22 09/17/2005 14:48:42
149 23 09/18/2005 14:48:42
150 24 09/19/2005 14:48:42
131 5 09/20/2005 14:46:50
151 25 09/20/2005 14:48:42
133 7 09/22/2005 14:46:50
152 26 09/21/2005 14:48:42
153 27 09/22/2005 14:48:42
154 28 09/23/2005 14:48:42
137 11 09/16/2005 14:46:50
155 29 09/24/2005 14:48:42
139 13 09/18/2005 14:46:50
156 30 09/15/2005 14:48:42
143 17 09/22/2005 14:46:50
145 19 09/24/2005 14:46:50
147 21 09/16/2005 14:48:42

17 rows selected.

SQL> select rownum, x, y, z from xyz;

ROWNUM X Y Z
---------- ---------- ---------- -------------------
1 127 1 09/16/2005 14:46:50
2 148 22 09/17/2005 14:48:42
3 149 23 09/18/2005 14:48:42
4 150 24 09/19/2005 14:48:42
5 131 5 09/20/2005 14:46:50
6 151 25 09/20/2005 14:48:42
7 133 7 09/22/2005 14:46:50
8 152 26 09/21/2005 14:48:42
9 153 27 09/22/2005 14:48:42
10 154 28 09/23/2005 14:48:42
11 137 11 09/16/2005 14:46:50
12 155 29 09/24/2005 14:48:42
13 139 13 09/18/2005 14:46:50
14 156 30 09/15/2005 14:48:42
15 143 17 09/22/2005 14:46:50
16 145 19 09/24/2005 14:46:50
17 147 21 09/16/2005 14:48:42


Here, ROWNUM does exactly what it’s supposed to do. It gives you a sequential number according to how the rows were pulled out of the table. There is obviously no logical order (to the human eye, anyway), but that’s how the rows come out. If you restrict by ROWNUM, you will certainly get a result, but probably not what you expect:

SQL> select * from xyz
2 where rownum < 6
3 /

X Y Z
---------- ---------- -------------------
127 1 09/16/2005 14:46:50
148 22 09/17/2005 14:48:42
149 23 09/18/2005 14:48:42
150 24 09/19/2005 14:48:42
131 5 09/20/2005 14:46:50


So what to do? Just put an ORDER BY in?


SQL> select * from xyz
2 where rownum < 6
3 order by y
4 /

X Y Z
---------- ---------- -------------------
127 1 09/16/2005 14:46:50
131 5 09/20/2005 14:46:50
148 22 09/17/2005 14:48:42
149 23 09/18/2005 14:48:42
150 24 09/19/2005 14:48:42


Nope. But we’re getting closer. You have to use your ordered query as a subquery and then apply the filter.

SQL> select * from (
2 select * from xyz
3 order by y
4 )
5 where rownum < 6
6 /

X Y Z
---------- ---------- -------------------
127 1 09/16/2005 14:46:50
131 5 09/20/2005 14:46:50
133 7 09/22/2005 14:46:50
137 11 09/16/2005 14:46:50
139 13 09/18/2005 14:46:50


Bingo. That’s what we wanted. Now suppose we wanted a “window” from 6 to 10:


SQL> select o.x, o.y, o.z from (
2 select rownum r, i.x, i.y, i.z from (
3 select x, y, z from xyz
4 order by y ) i
5 where rownum < 11 )o
6 where o.r > 5
7 /

X Y Z
---------- ---------- -------------------
143 17 09/22/2005 14:46:50
145 19 09/24/2005 14:46:50
147 21 09/16/2005 14:48:42
148 22 09/17/2005 14:48:42
149 23 09/18/2005 14:48:42


First/Last – in insertion order
Since there is no inherent way for you to figure out the order the rows were put into a table, this one is a little trickier. The only way you can positively indicate the order the rows were inserted into the table is if you explicitly mark each record. Sometimes this may be a “DATE_INSERTED” field or it may be some other field that indicates order. In my example, I have created a BEFORE INSERT trigger to populate the Primary Key (x) with a value from a sequence:


create trigger xyz_bi
before insert
on xyz
for each row
declare
pkval number;

begin
select xyz_id.nextval into pkval from dual;

:new.x := pkval;
end;


Therefore, I know my rows were inserted in the order according to X. I can then use my PK value to find out the last 10 rows inserted by:

SQL> select * from (
2 select * from xyz
3 order by x desc
4 )
5 where rownum < 11
6 /

X Y Z
---------- ---------- -------------------
156 30 09/15/2005 14:48:42
155 29 09/24/2005 14:48:42
154 28 09/23/2005 14:48:42
153 27 09/22/2005 14:48:42
152 26 09/21/2005 14:48:42
151 25 09/20/2005 14:48:42
150 24 09/19/2005 14:48:42
149 23 09/18/2005 14:48:42
148 22 09/17/2005 14:48:42
147 21 09/16/2005 14:48:42

10 rows selected.

Wednesday, September 14, 2005

Recruiting Jack of all Technology

I got a cold call from a recruiter this morning. I was feeling rather playful, so I persued the conversation.

Recruiter: Hi, this is MaryJo Recruiter from XYZ firm. Do you have a couple of minutes to talk to me?
Me: Um, OK.

R: I am looking for a combination Database Administrator/Database Developer/Business Analyst for a financial company.
Me: Let me stop you right there. What kind of money are we talking?

I always like to ask this question to see what the jobs out there are paying.

R: Well, they didn't specify a range. I asked if I found somebody at 80K would they want to look at them and the client said yes.
Me: Oh, so they're really looking for 65K-75K.
R: Probably.
Me: What kind of skills?
R: They want 5 years of Oracle DBA, 5 years using VB and/or MS Access, and business analyst background.
Me: That's a pretty strange combination.
R: The position is not defined very well.
Me: I see.
R: It's a really good company. Would you be interested?
Me: You'd have to triple the money.

I always like to throw out some factor like double or triple to see what they say.

R: Oh....I'll pay referral fees.
Me: I see. I know several people in the industry and know of a couple looking right now. Tell me more about the position.
R: This position would report to the CIO. They would interface with the users to define the project and then implement the project with VB and Access.
Me: OK, so where does Oracle come into the picture?
R: Well, they're thinking they might have a lot of data and they will need Oracle.
Me: Oh. I think you will have a very hard time filling this position. There's not that many BA's that know how to program in VB/Access and setup an Oracle Database.
R: Do you know of anyone that might fit this position? I pay referral fees.
Me: I have a couple people in mind, but they would be more on the technical side.
R: Great! Can I send you my contact information?

So if anybody wants to do VB/Access/Oracle DBA/Business Analyst for $70K in Connecticut, drop me a line.

Monday, September 12, 2005

Goodbye DBAs

Forrester Research has jumped on the "zero administration" database bandwagon. Looks like I'll be retiring in 2009. Woo hoo!!!

Oracle WTF

Stumbled upon a funny blog by Willam Robertson, Oracle WTF. It's modled after The Daily WTF, but with an Oracle slant. Check it out.

Friday, September 09, 2005

Keep it in your pants

You know that gap between the elevator and the floor you are on? Ever wonder what would happen if you dropped something down there? I have had that very thought on more than one occasion.

I had something to do after work today, and I was already late when I packed up and hit the elevator button. My keys were in my hand and I was fiddling with them when the elevator door opened. As I stepped into the elevator, my keys dropped from my hand; keys on the floor and keyless remote dangling over “the gap”. In what seemed like slow motion, I bent down to pick them and just then the remote won out and they slipped down the gap.

All I could muster was “Oh Shit” as they banged their way down to some unknown place.

I immediately went to the security office hoping they could do something. The security guard paged building maintenance and Darrel says “no problem”. We take the elevator to the bottom floor, send it back up, and he uses his special key to get in the elevator shaft. We look around, but there’s no keys down there. About $3 in change, countless gum and candy wrappers, but no keys. Darrel says “Are you sure you were in this elevator?” We repeat the same procedure floor by floor thinking that maybe they got hung up on somewhere in the shaft. I wasn’t worried about the keys, because the maintenance guy said the elevator people would come in Monday and do a better search. If we can’t find them, I’ll have to call my security officer and let him know I lost my keys. After hours. Then another thought hits me; how do I get home?

As we’re on our pursuit, Darrel says somebody drops something down the shaft about once a month, which surprises me. He suggests we go back down to the basement and look around again. We look in the same shaft and still no dice. He suggests we take a peek in the other elevator shaft with the possibility that the keys bounced around in the shaft. Sure enough, there they were. He smiles at me and says “You were in this one, weren’t you?”

The only problem was my keyless entry remote didn’t survive the drop. As luck has it, I keep my keys to the car inside the car and use the remote to open the door. Quickly, I realize I’m taking the train home tonight.

Moral of the story: Keep you keys in your pants until you get to your car.

Saturday, September 03, 2005

The Carolina Way

I love being at the beach. It gives me lots of time to catch up on non-technical reading. This year, I brought along The Carolina Way: Leadership Lessons from a Life in Coaching, by Dean Smith and Gerald Bell. A little disclosure here; I'm a Carolina fan and if Dean Smith said he made kids practice in bare feet, I'd think it was a good idea.

This book is more than your typical sports book. Sure, it's filled with stories about games, seasons, and individual players. Each chapter starts out with Coach Smith explaining a particular aspect of his coaching methodology. A Player's Perspective follows outlining how the particular aspect and Dr. Bell relates the aspect to the modern workplace. This book is all about building teams.

The Carolina Way can be summed up by these words: Play Hard; Play Together; Play Smart. For example, Coach Smith talks about recruiting players that will fit into his system. Dr. Bell then takes those ideas one step further and relates the recruiting to hiring workers. Another example is Dean Smith held regular one-on-one meetings with each of his players to discover their goals in life as well as basketball. Dr. Bell then outlines how you can pattern employee reviews around these same principals.

I personally got a lot out of this book and plan on implementing some of the strategies in my own team.

Thursday, September 01, 2005

Blogger for Word

I am posting this blog entry using the new Blogger for Word add-on.  I downloaded it a couple days ago and tried to get it working, but had problems connecting to blogger.com.  I fired it up today, and voila! It works.

I tried some of the other editors out there, but just found it easier to use the blogger.com editor, except in cases of posting source code.  Lets see how this comes out:

FOR i IN 1..100 LOOP
   i:= i+1;
END LOOP;

OK, that should suffice.  Heck, I already know Word, let me try this for a couple days to see how it goes.

Wednesday, August 31, 2005

Give 'em the finger

Saw an interesting piece of technology this week. You know those little cards you get at the supermarket that give you the sale price on advertised items? Well, the local Piggly Wiggly has gone high tech. When you sign up for that card you can tie it to a debit/credit card and they'll also scan your fingerprint. You pick up your groceries, proceed to the checkout, and scan you fingerprint to checkout.

Sunday, August 28, 2005

Traffic and Taxes

I've been in traffic hell the last two days. Friday I left work to go home, pack the car, and take off for 10 days on Kiawah Island near Charleston, South Carolina. Kiawah is our most favorite spot on Earth and we always look forward to winding down on the beach. I headed for home, but it took me 75 minutes to go 15 miles. I passed three fender benders which caused the traffic. Outside of Washington DC, we hit three more accidents that made the long drive even longer.

As I was sitting in traffic, I came up with two ideas. First, I think the driver at fault in a fender bender should have to pay the state a $3000 fine. You could call this a nuisance tax, a traffic tax, or a whatever tax. Either way, it comes directly out of the driver's pocket and would not be covered by insurance. This money, in turn, goes directly into the state's transportation budget and the state reduces the amount everybody else pays on gas by reducing the taxes. The person that caused the accident then gives a little back to the rest of us that were stuck in traffic while they were doing their lipstick going 75mph. If no fault can be assessed, then that's OK too.

Another idea that I came up with was I should be able to take the time I am sitting in traffic off my income taxes. Think about it for a second. The state AND federal government are responsible for having inadequate roadways that can't handle the amount of traffic that is on them. If every state were like New Jersey with its ten and twelve lane highways then nobody would take time off their taxes. If every state was like Connecticut or California and people deducted $10k off their taxes every year, the roads would get fixed. In a hurry.

Then again, maybe I had been breathing too much exhaust.

Thursday, August 25, 2005

Anything But Windows Revolution



So much for the Linux revolution. Only 4% of my readers use Linux as their desktop of choice (me, counting as 1%). That only slightly beats out MacOS X. How depressing.

Monday, August 22, 2005

Arrr, Hardware

Got home last Friday night and I couldn't connect to my wireless network. It sometimes happens and I have to reboot the WAP and/or router, so I didn't think anything about it. Once everything came back up, my laptop could see the WAP, but I couldn't aquire a network address through DHCP. There was no activity on the WAP so I thought something was screwy with my laptop. I tried my Wife's laptop, saw the WAP, but wasn't able to get a network address. Since both laptops are running Windoz, I figured I'd reboot everything and see what happens. Nothing happens.

I fired up my Linux box and started looking at the network. All seemed fine. I could ping the other hosts on the network, but couldn't ping the WAP (I think I can do that, but by now I am second guessing myself). So I reset both the router and WAP back to factory settings and changed all the static IP numbers on my network. Still no dice. At this point I figured my WAP is toast and I'll have to replace it. I have my eye on that little 4 port router with WAP attached anyway, so it will be a good chance to upgrade my technology. It's 23:30 and I need to go to a wedding tomorrow, so I give up.

While we were away Saturday the power went out at the house. Now, both the router and WAP are on a UPS, so they shouldn't have been affected. My wife fires up her laptop while I am on the desktop and says "When did you fix the wireless network?". WTF? Now it's working. I wonder if the WAP is running embedded Windoz?

Sunday, August 21, 2005

Mr. & Mrs. Johnson

Saturday we went on a road trip to Clifton Park, New York (outside Albany) for the wedding of our long-time friend, Denise and her soon-to-be, Randy. It looked like the weather was not going to cooperate as it rained just about the entire way. About 15 minutes before we pulled into Clifton Park, the clouds started breaking and the sun came out. What a perfect day for a wedding.

Denise and I worked together at the computer center when we were in school. Always bubbly and upbeat, you just knew she was one of those special people in the world. Denise also lived with my wife (then gf) when we were in school. I've always known Denise as a strong, independent, easygoing woman. When we met Randy about a year ago, the two seemed inseparable and make each other very happy.

All had a wonderful time at the wedding. The service was very unostentatious with a small bridal party, best man, and Pastor Chuck presiding over the nuptials. The reception was elegant and afforded lots of time to catch-up with friends. My only regret is that I would have liked to see Ann (another college friend) doing the YMCA, but alas, the DJ didn't serve that one up. Maybe that's just an '80s thing.

Just to show how thoughtful the newlyweds are, when we checked in to the Hotel there was a gift-basket containing snacks and drinks.

Congratulations to Mr. & Mrs. Johnson. May your new life be filled with happiness forever.

Friday, August 19, 2005

A New High

$2.899/gal this morning for gas. Over $33 to fill the Honda.

Wednesday, August 17, 2005

Installing Standard Edition

I installed 10.2.0.1 Enterprise Edition on RHEL 4 a couple weeks ago and got it working with a couple small tweaks to the OS. Yes, I know 10gR2 is not supported on RHEL 4 yet, but by the time I got to production it will be.

A couple days later I got a new project that will require 10.2.0.1 Standard Edition on RHEL 4. Not thinking about it, I used the installer that I downloaded from OTN to install. I chose the custom install (like I always do), de-selected the "Enterprise Options" and click,click,click, I was done.

Once I started the database things got interesting. My banner said:

sqlplus "/ as sysdba"
SQL*Plus: Release 10.2.0.1.0 - Production on Tue Jul 26 20:31:32 2005
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning and Data Mining options

OK, that's not cool. I de-install and re-install thinking maybe I screwed something up. Nope, same thing.

"Surely, someone has run into this problem before", I think to myself. I searched metalink for a while and noticed some bugs with the install so I created a TAR.

Me: I installed SE and came out with this banner. I did this, this, and this. I am seeing this, this, and this, which I think is incorrect.
TAR: Did you install from part # XYZ? There have been issues with that set of disks.
Me: No, I downloade from OTN.
TAR: Oh, install from CD.
Me: Isn't it the same software?
TAR: You downloaded from one of the first OTN downloads. There might have been a problem.
Me: But, I don't have the CD. I was told in TAR ### that CD's aren't ready for shipment.
TAR: create another TAR and tell them you need CDs because you're working on TAR ###.
Me: OK.


I create another TAR and get the CDs shipped to me. I copy from the CD to my local box and install from there. Same thing.

Me: OK, I installed from CD and got the same thing.
TAR: Ship us the installActions log and these other logs.
Me: Here they are.


Wait 3 days.

TAR: The log shows you installed EE. Install SE.
Me: No, I chose "Custom", unchecked the EE options, and installed.
TAR: Install SE.
Me: But that's the way I've done it before with no problems.
TAR: Install SE.
Me: But I don't want OEM, Apache, or any of the other stuff installed.
TAR: That's the way it's been done since Oracle7. That other stuff won't hurt, just install it.
Me: That's not true, but OK.


I saw this was going nowhere, so I closed the TAR. A few minutes later I get an email from Oracle Support saying my TAR has been updated.

TAR: Yeah, you're right. But the way you get around it is install everything for SE and then de-install the options you don't want.

Friday, August 12, 2005

Before Insert

One of my developers came to me with this problem. He is trying to manipulate the data coming into a table by using an INSERT TRIGGER. For the life of me, I can't figure out why this doesn't work (I must be missing something simple):

SQL> drop table xyz
2 /

Table dropped.

SQL> create table xyz
2 (
3 x number(10),
4 y varchar2(20),
5 z varchar2(4))
6 /

Table created.

SQL>
SQL> create or replace trigger xyz_bi
2 before insert on xyz
3 for each row
4 begin
5
6 :new.z := '777';
7
8 end;
9 /

Trigger created.

SQL>
SQL> insert into xyz values (1, '123456789','123');

1 row created.

SQL> commit;

Commit complete.

SQL> select * from xyz;

X Y Z
---------- -------------------- ----
1 123456789 777

SQL>

OK, so far so good. The trigger populated the Z value like I expected. However, when I pass in a string that is longer than the Z field, I get:

SQL> insert into xyz values (1, '123456789','123456789');
insert into xyz values (1, '123456789','123456789')
*
ERROR at line 1:
ORA-12899: value too large for column "JEFFH"."XYZ"."Z" (actual: 9, maximum: 4)


SQL>
SQL> commit;

Commit complete.

I can't explain why in a BEFORE INSERT trigger Oracle would care what the length of the string is before the trigger even fires. Any hints?

Before Insert, Part II


Heath Sheehan said...

Row level triggers fire for each row that is affected by the triggering statement. That would imply that all validity checks have to be passed before the triggers fire. Any explicit or implicit data conversions have to result in valid data. Any check constraints have to pass, etc.

If the data isn't valid, the row isn't inserted and there's nothing for which the trigger should fire.


Yeah, but if that's true, why doesn't a NOT NULL constraint elicit the same behaviour?


SQL> drop table xyz
2 /

Table dropped.

SQL> create table xyz
2 (
3 x number(10),
4 y varchar2(20),
5 z varchar2(4) not null)
6 /

Table created.

SQL>
SQL> create or replace trigger xyz_bi
2 before insert on xyz
3 for each row
4 begin
5
6 IF :new.z IS NULL THEN
7 :new.z := '777';
8 END IF;
9
10 end;
11 /

Trigger created.

SQL>
SQL> insert into xyz values (1, '123456789','123');

1 row created.

SQL> commit;

Commit complete.

SQL> select * from xyz;

X Y Z
---------- -------------------- ----
1 123456789 123

SQL>
SQL> insert into xyz values (1, '123456789',NULL);

1 row created.

SQL>
SQL> commit;

Commit complete.

SQL> select * from xyz;

X Y Z
---------- -------------------- ----
1 123456789 123
1 123456789 777

SQL>

Wednesday, August 10, 2005

Job Shock

I read with interest IT Workers Confront 'Job Shock' by Thorton A. May in the latest Computerworld. Mr. May brings up a very valid point that users have extraordinary tools in their hands to do the work that IT Professionals used to do. It brought to mind one of my first programming projects way back in 1989. I was to write a program that pulled data out of an Ingres database, sort and group it in illogical ways, and spit it out on a landscape page. The tool of choice was C and it took me six weeks to write. Today, I'd dump some summarized data in a CSV file and let the user mess with it.

Mr. May goes on to wonder "What if paid IT employment was to steadily disappear?". Good question. I can tell you the people at my first job are still employed. No, they're not writing too many C programs anymore, but web interfaces. They give their users the tools to get their own data. Last I knew, there were just as many people as when I left.

Mr. May goes on to point out how to "save" your fat IT career. All his points are well taken, but the bottom line is your career is your business. If you let you business stagnate, it will die. (If this sounds familiar, thanks for reading).

Tuesday, August 09, 2005

dbms_stats.alter_database_tab_monitoring

While implementing my new statistics gathering procedure in production, I ran into a snag with permissions.

The very first step in my process is to setup my "analyze" user and create a job that runs every night that turns MONITORING on for any new tables. Sounds simple enough, right?


$ sqlplus "/ as sysdba"

SQL*Plus: Release 9.2.0.5.0 - Production on Tue Aug 9 21:14:18 2005

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.5.0 - Production
With the Partitioning option
JServer Release 9.2.0.5.0 - Production

SQL> create user analyzer identified by youbetcha
2 temporary tablespace temp
3 default tablespace tools
4 quota unlimited on tools;

User created.

SQL> grant create session to analyzer;

Grant succeeded.

SQL> grant alter session to analyzer;

Grant succeeded.

SQL> grant execute on dbms_stats to analyzer;

Grant succeeded.


OK, that should be enough, right? Let's give it a try.


SQL> connect analyzer/youbetcha
Connected.

SQL> exec dbms_stats.alter_database_tab_monitoring(TRUE);
BEGIN dbms_stats.alter_database_tab_monitoring(TRUE); END;

*
ERROR at line 1:
ORA-20000: Insufficient privileges or does not exist
ORA-06512: at "SYS.DBMS_STATS", line 10733
ORA-06512: at "SYS.DBMS_STATS", line 10752
ORA-06512: at line 1


Hmm. I looked at the package spec and find out it's run with Invoker Rights and not Definer's rights. Ah, I must need "ANALYZE ANY" privilege.


SQL> grant analyze any to analyzer;

Grant succeeded.


That must be it, let's try it again.


SQL> connect analyzer/youbetcha
Connected.
SQL> exec dbms_stats.alter_database_tab_monitoring(TRUE);
BEGIN dbms_stats.alter_database_tab_monitoring(TRUE); END;

*
ERROR at line 1:
ORA-20000: Insufficient privileges or does not exist
ORA-06512: at "SYS.DBMS_STATS", line 10733
ORA-06512: at "SYS.DBMS_STATS", line 10752
ORA-06512: at line 1


OK, that ain't it. I did some research on Metalink, AskTom, and poured through the docs, but didn't really get anywhere. Then I thought, how about a Trace? I started a level 12 trace and then re-executed the procedure. Under the covers, dbms_stats.alter_database_tab_monitoring just does a "ALTER TABLE xyz MONITORING". I get it, I need "ALTER ANY TABLE".


SQL> connect / as sysdba
Connected.
SQL> grant alter any table to analyzer;

Grant succeeded.

SQL> connect analyzer/youbetcha
Connected.
SQL> exec dbms_stats.alter_database_tab_monitoring(TRUE);

PL/SQL procedure successfully completed.



Simple as that. Now all I have to do is create a dbms_job and run it every day:

SQL> declare
2 lJobNo INTEGER;
3 lJob VARCHAR2(2222);
4 begin
5
6 lJob := 'dbms_stats.alter_database_tab_monitoring(monitoring=>TRUE);';
7 dbms_job.submit(
8 job=>lJobNo,
9 what=>lJob,
10 next_date=>SYSDATE,
11 interval=>'trunc(sysdate+1)');
12
13 dbms_output.put_line('job submitted: ' || to_char(lJobNo) || '...');
14
15 end;
16 /

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> select job, last_date, last_sec,
2 next_date, next_sec, broken,
3 failures, what
4 from user_jobs;

JOB LAST_DATE LAST_SEC NEXT_DATE NEXT_SEC B FAILURES WHAT
---------- --------- -------- --------- -------- - ---------- -------------------------
23 09-AUG-05 21:39:31 10-AUG-05 00:00:00 N 0 dbms_stats.alter_database_tab_monitoring(monitoring=>TRUE);


That's done, on to the next step!

Monday, August 08, 2005

Thanks Doug

I'd been lurking in Doug Burns' Blog for a couple of weeks and found most of his posts interesting. Interestingly, enough, that last week I put a link on my blog to his so I could share with others. Then he goes and changes blog hosts. Now I have to update my blog links to include his new site. Thanks Doug.

Thursday, August 04, 2005

Oracle Job Scheduling

Managing jobs in Oracle 9i and below was a pretty straight forward process; start the job queue processes and submit a PL/SQL Block using dbms_job. If you wanted any level of detail you had to wrap the dbms_job functionality around a package and some other tables. Job scheduling was basic and it generally worked. Any complicated scheduling or interaction with the OS and you were out of luck and had to revert to cron.

I'm not sure what I expected when I started reading Oracle Job Scheduling, by Dr. Tim Hall, a few days ago. I've got a decent handle on dbms_job and have used it extensively for all sorts of maintenance tasks. How different could job scheduling in 10g be? Believe me, it's different.

Dr. Hall explains how the new dbms_scheduler packages works and the details of each call. In addition he explains in detail how the new INTERVAL type works and gives very through examples. Chapter 4 is by far the most valuable chapter as it explain four different methods to schedule dependant jobs. The examples in this chapter are an extension of Tims experience in the real-world implementing solutions. Later on, the book explains about how to monitor the new scheduler and how to view the job logs.

I've got to admit I breezed over the sections on OEM and OS Scheduling. I don't use OEM and there's nothing I really need to know about cron.

This book was a good read. Don't get me wrong, it's no A Dog Year: Twelve Months, Four Dogs and Me. It's a technical book through and through. I would definitely recommend it if you are planning on using the Oracle job scheduler to implement complex business schedules.

Oh, and by about the third time I saw:

-- ****************************************************
-- Copyright 2005 by Rampant TechPress
-- This script is free for non-commercial purposes
-- with no warranties. Use at your own risk.
--
-- To license this script for a commercial purpose,
-- contact info@rampant.cc
-- ****************************************************
I was ready to barf.

Wednesday, August 03, 2005

Hiring Round 3

I can't freakin' believe it! You may recall my sagas about finding a new Database Administrator (here and here). Well, it's happened again.

We interviewed about 20 canidates for the open DBA position and narrowed the choice down to two very qualified candidates. Each had their own strengths and weaknesses. It was a difficult choice, but we finally chose one over the other and offered NewDBA2 the position. NewDBA2 thought about it for about an hour before he called us back to accept the position. A start date was scheduled for two weeks from the next Monday. As recently as yesterday, NewDBA2 contacted the recruiter and reiterated his excitement for the new position. In fact, they decided to go to lunch today to celebrate.

Then I got the call. NewDBA2 is sorry to say that he has been offered another position and can't start on Monday. Yes, Monday, three days away.

That's twice in a row. I had two people commit to a job and at the last minute they backed out. Personally, if I don't like something about the job, I just don't take it. Sigh, back to the recruiters.

Sunday, July 31, 2005

Around New York

The Wife and took in a matinee showing of The Lion King (which was spectacular) on Saturday. Our plan was to take a train to New York City, see the show, go to dinner, and be back by the evening. We've been to New York a number of times the last five years, so we've gotten pretty good at getting around town. We're used to crowds, pan handlers, and those annoying hawkers trying to get you on a bus tour.

As we exited the theatre in Times Square, there were lots of people around. I mean LOTS of people. Seems some dingbats with cameras were filming a "man on the street" type reality TV show about fashion faux-pas and everybody wanted to be on. We only had to go four blocks to get to our restaurant and 15 minutes to get there. Fashionistas in training were swarming the cameras so we crossed the street - right under the MTV studio. Some guy was giving away CD's and of course the TRL crowd was grabbing them up by the second. We weren't encouraged by the 1/2 block line outside Bubba Gump, but still pushed on. The next block, we traveled down 45th street and got out of Times Square. A block later it was like a ghost town with hardly any people at all. We finally got to the restaurant only 5 minutes late and had a great dinner and still was able to catch the train at a decent hour. Except for the crowds, a great day.

Friday, July 29, 2005

Parking

We were out and about the other day running errands in one of the snooty towns on the Gold Coast when we came upon a Carvel. I'm never one to pass up Carvel, so I flipped my blinker on and started pulling into the parking lot. The Tahoe driving lady ahead of me started taking a spot right up front, but half way in, she stopped and started backing up. Meanwhile, I'm half in the lot and my tail end is sticking out into a four lane road. I mumble "WTF?" as Mrs. Tahoe just about backs into me and takes another spot in the lot. Ah, she saw it was a handicap spot. My wife looks at me with rolled eyes like I did something wrong. As as last ditch effort to save my manhood I mumble "That's how people get killed." (Jesus, that was stupid. People get killed from a fender bender, yeah right.)

So Mrs. Tahoe gets out with Grandma Tahoe and her brood and goes to the counter. I notice one of the little Tahoes wants to get behind the counter and touch everything. Then he's looking up and around and Grandma Tahoe quietly grabs his hand. The other two little Tahoes are ordering their ice cream; one with sprinkles, one with chocolate syrup. Mrs. Tahoe orders a fat-free cup (no sprinkles) for herself, a chocolate cup for Grandma (chocolate sprinkles), and a cup (no sprinkles) for Jr. Tahoe.

When we make it outside the Tahoes are enjoying their ice cream. Jr. Tahoe has barely touched his and is looking around touching this and that. I get it, Jr. Tahoe is probably challanged. I feel even worse about the parking as Mrs. Tahoe can probably justify parking in the handicap spot. We finish our ice cream, Jr. Tahoe looks at me and I smile back. He smiles. Cool, maybe he forgives me at least.

As we're walking out past "the" parking spot, Grandma-but-I-won't-admit-it Mercedes pulls into the handicap spot, jumps out of the S-class and heads for the counter. I shake my head knowing I'll get another look if I say something. Out of the blue, my wife says, "Um, you know that's a handicap spot, don't you?" Grandma-but-I-won't-admit-it Mercedes looks startled but heads back to the car and looks at the handicap signs as plain as day. She keeps circling the car like the signs will magically disappear until the Tahoes get up and head for their troop transport. Grandma Tahoe shoots her a look but Mrs. Tahoe is too busy tending to messy faces to notice. Busted. As we're pulling out, I see Grandma-but-I-won't-admit-it Mercedes slinks back to the car and moves it.

"That's how people get killed", I mutter to a smirk.

Thursday, July 28, 2005

SA Superhero

Fortunately for me, the best SA was on-duty. After the server rebooted itself about 6 times, we decided to swap it out for a spare. A couple disk swaps, attach the SAN filesystems and we were back in business in 2 hours.

Waiting on my SA

One of my DB Servers is in a perpetual state of rebooting. Waiting for the SA to figure out what's going on. [sarcasm]I love being on-duty.[/sarcasm]

Wednesday, July 27, 2005

The price you pay

The price of gas never really bothered me. You see, my everyday car is a Honda Civic and I typically get about 37 miles per gallon. Sometimes more, sometimes less. I used to drive about 130 miles a day back and forth to work and it cost about $50 a week for gas. I recently moved and now a tank of gas lasts me about 10 days. My wife drives half the miles I do and when I fill her 4 Runner's tank it's always been $35 or more.

I filled my tank tonight and the pump clicked off at $30.03. $30.03 to fill a Honda! I've always put more than $20 in, but I'd never broken $30. I know at $2.599/gallon we've got nothing to complain about in the U.S. compared to other parts of the world, but sheesh. This is starting to cut into my mad money.

Thursday, July 21, 2005

Preparing for "Oracle Job Scheduling"

I got Tim Hall's new book, Oracle Job Scheduling, a day after I started installing Oracle 10g R2 on my home system. I had just installed it at work on one of my development systems and it was literaly click, click, click, done. I figured it would be about the same at home and I could start learning about the job scheduler that night. Started downloading from OTN and, BAM, filesystem filled up. I guess it wouldn't hurt to remove the old software, right? First I removed 9.2.0.5 and only freed up about a gig. I won't need 10.1 now that I got 10.2, right? So I removed it. Then I started the 10.2.0.1 install and the software seemed to install OK. During the database creation, however, I got an ORA-12547 message. I checked out a couple things on metalink and figure my libaio needs to get updated. Off to rhn.redhat.com to get the right rpm and install it. Then I remake oracle. Finally, oracle starts and I can create the database.

I know I promised Tim I'd review his book on my blog. I open up the book to an icon caricature of Don Burleson giving me a gigantic "thumbs up". I can't deal with this, I'm going to bed.

Tuesday, July 19, 2005

There's a storm a brewin'

Seems as though DBASupport.com has compromised the presentation of their forums in order to accomodate advertisers and their own interests. Several members (myself included) have expressed their dislike of the new format. Join in on the discussion and tell us what you think.

Monday, July 18, 2005

Futzing with 10g Connection Manager

I'd like to think I'm somewhat proficient using Oracle's Connection Manager product. After all, I first implemented CMAN in 8.1.6 for connection pooling. I then used CMAN to receive database traffic through a firewall. Pretty simple stuff, but I could get it to work. Sure, CMAN had it's shortcomings, but there were ways around most of them. Migrating through the 9i versions was no problem; install the cman in a new $OH, copy the cman.ora file and start it up. No fuss, no muss.

While investigating a related connection problem, Oracle Support suggested testing my problem on a 10g connection manager. "No problem", I said confidently knowing the 9i upgrade was a piece of cake. Lets just say things are a little different in the 10g world. Oracle has addressed some of CMAN's shortcomings in the latest release. Below are some of the things I encountered during my upgrade.

Looking through a new window
The first thing I noticed was cmctl now has a new set of commands. I found myself doubting that I ever ran connection manager in the past. A quick review of the 10g connection manager architecture gave me a foundation for the new environment.

CMAN.OR Migration
10g comes with this neat little tool called cmmigr. I used this tool to migrate my working cman.ora file into a non-working cman.ora file.

Tweaking cman.ora file #1
Here's where the real work came in. I had to tweak my cman.ora file to include my host's IP number. One of the nice things about 9.2 cman was you could use (HOST=) in your configuration and the connection manager would automatically use the hostname it was running on. I used this to my advantage to have a generic cman.ora file that I could run on multiple hosts. Since this is only a test, I'll leave this mystery for later.

Starting CMAN
I have what I think is a correct cman.ora file now. I start cmctl and issue the ADMINISTER command. No problem. I then try to STARTUP the connection manager and get a "TNS-04012: unable to start Oracle Connection Manager". The next thing you learn is there is an alert.log file in $OH/network/log that you can check. I had a couple errors in my alert.log that looked like:
(LOG_RECORD=(TIMESTAMP=18-JUL-2005 11:43:35)(EVENT=Failed to start listener process)(REASON=)(OPN=65)(NS1=12545)(NS2=12560)(NT1=515)(NT2=2))
Hmm, nothing useful there. I go back to the docs and see that cman now needs a listener to pass connections off to cmgw. So I try to start a listener process on my own and realized there was no lsnrctl in $OH/bin. Aha! I only installed cman and not the listener. Install the listener components and retry. SUCCESS!

Thursday, July 14, 2005

Statcounter



Just after Tom Kyte put a link on his blog to mine, I decided to put a counter on the page. I looked at a couple other sites I respected and I decided to use statcounter from www.statcounter.com. My statcounter looks just like Tom's (except maybe the scale is off by a factor of 100!)

Firefox 1.0.5

Learned from my statcounter that some of you are using Firefox 1.0.5. I didn't know it was even out yet! Needless to say, I upgraded right away.

Saturday, July 09, 2005

The Search Continues...

You may remember that I had been looking for a DBA to add to my team a few weeks ago. I offered the position to New DBA and basically met everything he was asking for. A few days before New DBA is supposed to start, he calls and says "Sorry, not interested anymore." Seems NewDBA has found a better situation for himself. So it's back to the recruiters sending 10 resume's a day, sigh... Maybe I'll have some good answers to my tech questions to post.

Thursday, July 07, 2005

ORA-22856

Putting some changes into a 9.2.0.5 database. The table is range partitioned, but only has two simple fields (not object types). Adding columns in QA and DEV worked just fine, although they didn't have the volume that PROD does. I run my script and find:

ALTER TABLE xyz ADD (z varchar2(20));

ORA-22856: cannot add columns to object tables

WTF?? Oh yeah, production uses COMPRESS. Chalk it up to bug 2421054, adding columns to a compressed table. Can you say patch anyone?

Wednesday, July 06, 2005

Monday, July 04, 2005

Trace it!

I often give people the advice to trace their SQL session to see what is going on. Knowing how to trace is one of the tools that every developer and DBA should have in their toolbox.

By tracing, I mean capturing all the SQL in a user's session, or a SQL trace. As you can tell from the Oracle Performance Tuning Guide and Reference, there are many ways to enable tracing. Below are two methods I use as a DBA in my day-to-day routine.


Turn tracing on

Enabling tracing in your own session is very easy. From SQL*Plus, just alter your session and set sql_trace=true.
SQL> alter session set sql_trace=true;

Session altered.

Dude, you're tracing. Every query you run from now on will go into a trace file until you set sql_trace to false.

Sometimes, a session is already underway and you need to start a trace midway through it's execution. Here, you need to be a DBA and enable the 10046 event in the user's session. This is a little more tricky since you have to know the sid and serial# from v$session in order to enable the tracing. For example,


SQL> select sid, serial#, username
2 from v$session
3 where username = 'JEFFH';

SID SERIAL# USERNAME
---------- ---------- ------------------------------
25 5 JEFFH

SQL> exec sys.dbms_system.set_ev(25, 5, 10046,8,'');

PL/SQL procedure successfully completed.


Statements get written to the trace file when they are first encountered after the trace is started. The statement that is executing may not be in the final trace file.

If you are using dedicated servers, the trace file will typically be in the directory on the server specified by the user_dump_dest parameter in the init.ora file. If you are using shared servers, the trace file will be in the directory specified by background_dump_dest. The trace file will have your server process id in it somewhere. If you are running the trace on your own session, you can create a stub with the ALTER SESSION SET TRACEFILE_IDENTIFIER=xxx; This way, your file will have the "xxx" name in it and be easier to identify.


What do I do with it?

OK, so you got this big file. It's just a simple ASCII file, go ahead and read it.

Nah, just kidding. Oracle gives you this program called TKProf that reads your trace file and spits out somewhat meaningful output. (I think Oracle uses tkprof as an acronym for Tom Kyte, Professor, but that's just speculation on my part.) My favorite option to tkprof is sort=prsela,fchela,exeela. This sorts the statements from longest running to shortest running. I prefer this format because I can concentrate on the top two or three statements for the most impact.


SQL> alter session set sql_trace=true;

Session altered.

SQL> alter session set tracefile_identifier='mytest';

Session altered.

SQL> select count(*) from xyz_master;

COUNT(*)
----------
4

SQL> select count(*) from xyz_detail;

COUNT(*)
----------
16

SQL> exit

D:\oracle\product\admin\xp10\udump>dir *mytest*
Volume in drive D has no label.
Volume Serial Number is 70AE-6E52

Directory of D:\oracle\product\admin\xp10\udump

07/04/2005 08:11 PM 38,785 xp10_ora_1544_mytest.trc
1 File(s) 38,785 bytes
0 Dir(s) 14,751,383,552 bytes free

D:\oracle\product\admin\xp10\udump>tkprof xp10_ora_1544_mytest.trc foo.out sort=prsela,exeela,fchela

TKPROF: Release 10.1.0.2.0 - Production on Mon Jul 4 20:16:53 2005

Copyright (c) 1982, 2004, Oracle. All rights reserved.

D:\oracle\product\admin\xp10\udump>dir /od foo.out
Volume in drive D has no label.
Volume Serial Number is 70AE-6E52

Directory of D:\oracle\product\admin\xp10\udump

07/04/2005 08:16 PM 21,368 foo.out
1 File(s) 21,368 bytes
0 Dir(s) 14,751,358,976 bytes free

D:\oracle\product\admin\xp10\udump>




Things you will want to know about tracing

  • The timed_statistics init.ora parameter must be set to true in order to get any meaningful timing data out of tkprof.

  • I prefer to set the max_dump_file_size init.ora parameter to either unlimited or a really large value (2G). If you're going through the extra work of tracing a process you don't want to potentially miss the most important part.

For more details

Oracle-Base.com
Pete Finnigan

Friday, July 01, 2005

WEP #2


Some people just don't get it. I'm here at a laundromat and working on a post for my blog when my wireless icon tells me I'm connected to "linksys-secure". Secure. Yeah right.

Thursday, June 30, 2005

Oracle 10g R2

It's June 30th. The day is half done. I've got Linux. Where's 10gR2?

Wednesday, June 29, 2005

Deferrable Constraints

One of my developers asked me how to drop a constraint in PL/SQL today. "Don't do that" was my immediate answer.

Well, seems they have a legitimate case where they need to change a PK value in a controlled environment. I suggested deferrable constraints would give them some flexibility yet still maintain data integrity at the transaction level. I quickly got in over my head when peppered with questions about how it works and when errors are raised. So I cobbled up an example:

Lets create a master-detail relationship:

SQL> @c:\temp\create_tables.sql
SQL> drop table xyz_master cascade constraints;

Table dropped.

SQL> create table xyz_master (
2 id number(10) primary key,
3 hire_date date,
4 common_name varchar2(200))
5 /

Table created.

SQL> drop table xyz_detail cascade constraints
2 /

Table dropped.

SQL> create table xyz_detail (
2 id number(10),
3 pay_date date,
4 pay_amount number(20,3),
5 constraint xyz_detail_pk
6 primary key (id, pay_date))
7 /

Table created.


Now, lets put the deferrable fk on...

SQL> @c:\temp\add_constraints
SQL>
SQL> alter table xyz_detail
2 add constraint xyz_master_fk1
3 foreign key (id)
4 references xyz_master(id)
5 initially deferred deferrable
6 /

Table altered.


Lets test to make sure the RI still works...


SQL> @c:\temp\add_bad1.sql
SQL> insert into xyz_detail values (1, trunc(sysdate), 100);

1 row created.

SQL> insert into xyz_detail values (1, trunc(sysdate+7), 100);

1 row created.

SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02291: integrity constraint (JEFFH.XYZ_MASTER_FK1) violated - parent key
not found

Ah, I get it. The rows were added, but when I committed, the RI was checked at that point. Lets make sure the tables are clean...


SQL> select * from xyz_detail;

no rows selected

SQL> select * from xyz_master;

no rows selected


OK, that's what I expected. Lets put in some "real" data in the correct order.


SQL> @c:\temp\add_good1.sql
SQL> insert into xyz_master values (1, trunc(sysdate-365*5), 'jeff hunter');

1 row created.

SQL> insert into xyz_master values (2, trunc(sysdate-365*3), 'fred flintstone');

1 row created.

SQL> insert into xyz_master values (3, trunc(sysdate-(365*12.2)), 'mr. slate');

1 row created.

SQL> insert into xyz_detail values (1, trunc(sysdate), 100);

1 row created.

SQL> insert into xyz_detail values (1, trunc(sysdate-7), 100);

1 row created.

SQL> insert into xyz_detail values (1, trunc(sysdate-14), 100);

1 row created.

SQL> insert into xyz_detail values (1, trunc(sysdate-21), 100);
1 row created.

SQL> insert into xyz_detail values (2, trunc(sysdate), 22.22);

1 row created.

SQL> insert into xyz_detail values (2, trunc(sysdate-7), 22.22);

1 row created.

SQL> insert into xyz_detail values (2, trunc(sysdate-14), 22.22);

1 row created.

SQL> insert into xyz_detail values (2, trunc(sysdate-21), 22.22);

1 row created.

SQL> insert into xyz_detail values (3, trunc(sysdate), 1000);

1 row created.

SQL> insert into xyz_detail values (3, trunc(sysdate-7), 1000);

1 row created.

SQL> insert into xyz_detail values (3, trunc(sysdate-14), 1000);

1 row created.

SQL> insert into xyz_detail values (3, trunc(sysdate-21), 1000);

1 row created.

SQL> commit;

Commit complete.

I expected that to work. Lets try to insert the details and then the master record just for kicks.

SQL> @c:\temp\add_wrong_order1.sql
SQL>
SQL> insert into xyz_detail values (4, trunc(sysdate), 100);

1 row created.

SQL> insert into xyz_detail values (4, trunc(sysdate-7), 100);

1 row created.

SQL> insert into xyz_detail values (4, trunc(sysdate-14), 100);

1 row created.

SQL> insert into xyz_detail values (4, trunc(sysdate-21), 100);

1 row created.

SQL> insert into xyz_master values (4, trunc(sysdate-(365*12.2)), 'barney rubble');

1 row created.

SQL> commit;

Commit complete.

That's cool, that works. Lets try to violate the PK to see what happens.
SQL> @c:\temp\add_bad2.sql
SQL> insert into xyz_detail values (1, trunc(sysdate), 100);
insert into xyz_detail values (1, trunc(sysdate), 100)
*
ERROR at line 1:
ORA-00001: unique constraint (JEFFH.XYZ_DETAIL_PK) violated


SQL> insert into xyz_detail values (1, trunc(sysdate-7), 100);
insert into xyz_detail values (1, trunc(sysdate-7), 100)
*
ERROR at line 1:
ORA-00001: unique constraint (JEFFH.XYZ_DETAIL_PK) violated


SQL> insert into xyz_detail values (1, trunc(sysdate-14), 100);
insert into xyz_detail values (1, trunc(sysdate-14), 100)
*
ERROR at line 1:
ORA-00001: unique constraint (JEFFH.XYZ_DETAIL_PK) violated


SQL> commit;

Commit complete.

Right. The PK is not deferrable. Lets change the PK value in the master and then change the PK value in the detail records.
SQL> @c:\temp\update1.sql
SQL> update xyz_master set id=10 where id=1;

1 row updated.

SQL> update xyz_detail set id=10 where id=1;

4 rows updated.

SQL> commit;

Commit complete.

That works, my developer will be happy. Just for kicks, lets try it the other way around.

SQL> @c:\temp\update2.sql
SQL> update xyz_detail set id=20 where id=2;

4 rows updated.

SQL> update xyz_master set id=20 where id=2;

1 row updated.

SQL> commit;

Commit complete.

Of course that works. Let's just double check to make sure RI stays in tact (like we have any doubts now)...


SQL> @c:\temp\update3.sql
SQL> update xyz_master set id=30 where id=3;

1 row updated.

SQL> update xyz_detail set id=30 where id=4;

4 rows updated.

SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02292: integrity constraint (JEFFH.XYZ_MASTER_FK1) violated - child record
found


Sure enough. I still need to investigate any locking issues, but it looks like this might be what we are looking for in this particular case. If anybody has any hints on what to look out for, I'd appreciate it. Also, assuming we have a legitimate business case, do you think this is an appropriate use of deferrable constraints?

Monday, June 27, 2005

Process or No Process?

My wife and I are both in the IT world. She is a project manager that for most of her working life has been employed by Fortune 100 companies. She works on software products that go into the marketplace and whose teams are counted in hundreds. It can be years before a product gets from conception to the marketplace. Her products are developed for the target business community.

I, on the other hand, am a somewhat knowledgeable Oracle guy whose biggest company has been 400 people if you count the part-timers. I work on projects where most times the number of team members can be counted on one hand. If we come up with an estimate of 3 months, management says "break it up" to shorten the development time. My projects are all internal endeavors whose primary goal is to help the business make more money or be more efficient.

She has always been an advocate of following a development process. A development process allows you to measure the progress of each phase of the project. Each phase of the project is defined by the work product that must be produced by the end of phase. If the process is followed, the resulting product has been good quality and usually makes a lot of money for the company.

My group works in what you could call the 80/20 methodology. We try to get 80% of the functionality into the users hands in a very short time. The other 20% of functionality will come when the business decides spending time developing a solution is cost effective and will bring greater benefit to the business than not doing it. Each project is short in nature and its success can be measured in how much time we can save or how much revenue it brings in. Sure, we have bugs in our software, but who doesn't?

At the end of the day, we both get the job done. She tries to explain how the process works even when milestones are not met. She can tell you how much of the phase is complete and the day it will be done with about 95% accuracy. I don't get it. I try to explain how my users expect results, not a document explaining what we will do. We need to solve a problem today, not 14 months from today. She doesn't get it. Is software utopia in the middle somewhere?

Thursday, June 23, 2005

Does Certification Matter Anymore?

I just finished the hiring process for my new DBA scheduled to start in a couple weeks. I was looking for a particular type of person to fill a particular role who would have the initiative to grow as the business grew. I've hired a few people in my time; made a couple mistakes, underestimated a couple of people. Overall, though, I've been pretty successful at evaluating individuals for the positions I want to fill. When I assembled the skills inventory to send to recruiters for my open DBA position it didn't even cross my mind to make certification required. When I look at resumes I certainly notice if the candidate is certified or not, but it doesn't sway my decision either way.

After I got through with this whole process, I read with interest Don Tennant's article (Certifiably Concerned) in Computerworld about how certified professionals get smaller pay increases than their un-certified counterparts. Don says:
So if you perceive even a subtle cultural shift away from certification in your organization, do something about it. In the process, you'll be keeping the bar up high where it belongs.

I thought to myself for a second "Am I part of the problem?". I looked at the study, but was left with more questions than answers. For example, What was the pay differential between certified and non-certified employees? Does becoming certified in technology XYZ bring an immediate pay raise?

The big question is does certification deliver any real value to the business? I think it does, but probably not for the same reasons as Don Tennant thinks. When a person starts the certification process she proves to me that she has drive and wants to learn more. A DBA shows me they want to understand what's going on instead of just reacting to the situation. I know it's just a couple of tests, but when a person dedicates themself to a goal and achieves it, I know I want them to work for me.

On the other hand, I don't need certification to show me that either. I love it when a DBA questions how things are setup and can argue why things should be different. The guy that stays all night through 8 recoveries even though he has only been there 3 weeks is someone you want to hang on to.

Apparently, Don got lambasted for his comments. I disagree that you need a "consistent, quantifiable means of documenting the skills assets of your IT organization. Otherwise, expanding and improving that institutional skill set will be an adhoc activity, and efforts to optimize the quality and productivity of your workforce will suffer". I know the quality of my workforce by two easily measured metrics; how many mistakes they make and how many service interruptions my systems have had. I know the productivity of my workforce by how long it takes them to perform a given task the first time. If it's a repetitive task, we automate it.

Certification has it's place, but experience and drive trump certification every time.

Tuesday, June 21, 2005

Oracle Wait Interface

The conventional wisdom of tuning Oracle databases has undergone a shift in the last five or so years. Tuning an Oracle database used to mean measuring various ratios and tweaking init.ora parameters so the ratios were "right". Maybe the users' response got better, maybe it didn't. But the database was running better. Some people built businesses around this black magic of tweaking init.ora parameters so the numbers looked "right" without ever really solving the root cause of the slowness. Tuning today means resolving the root cause of the problem and making the users response time acceptable. A large part of measuring performance is the Oracle Wait interface.

I first learned about the Oracle Wait Interface (OWI) was in an informal conversation with Roman Kab from Optimum Computing after a users group meeting. Roman talked about some new 8i features and he mentioned the OWI and the higher degree of instrumentation. One of the most helpful parts of the new "Statspack" report was the top wait events. I thought it was interesting that my instance waited on "db file sequential read" the most, but what statement caused the majority of these waits? I had a good buffer cache hit ratio, so it couldn't be that relavent, could it?

For me, tkprof in version 9.2 brought the OWI to the forefront of wait event tuning. The ability to see which waits were affecting the longest running statements was a major step in diagnosing performance problems. I experience several "Ah-ha" moments when I looked the new 9.2 traces.

Two new books on the OWI are Optimizing Oracle Performance by Cary Milsap and Oracle Wait Interface: A Practical Guide to Performance Diagnostics & Tuning (OWI:Practical) by Richmond Shee, Kirtikumar Deshpande and K. Gopalakrishnan. Optiming Oracle Performance is a roadmap of how to use information garnered from OWI and use it in a reproducable scientific method to solve performance problems. In this book, the author explains queueing theory in terms most DBAs can understand. In Oracle Wait Interface: A Practical Guide to Performance Diagnostics & Tuning by Richmond Shee, Kirtikumar Deshpande and K. Gopalakrishnan, the authors delve into each of the most populate wait events and explain in detail how to diagnose and resolve the different conditions which might cause excessive waits. Where Optimizing Oracle Performance was heavy in the theories behind the OWI, OWI:Practical concentrates on how to use the wait events in everyday situations.

I suggest reading OWI: Practical first to get a handle on how the wait interface works and how it can help you in day-do-day performance tuning. Once you get some practice with OWI and tkprof, you will be able to better comprehend Optimizing Oracle Performance and appreciate the performance tuning methodlogy outlined in it.

Tuesday all ready?

Geez. It's Tuesday all ready. I haven't posted anything worthwhile in a week. Keeping up this blog is a little more work than I bargained for. I've just finished two books on OWI and will be reporting on them shortly.

Thursday, June 16, 2005

DBASupport.com Forums Back Up

Seems they've gone through an upgrade.

DBASupport.com Forums Down?

They seem to be down for me. From http://www.dbasupport.com/forums:

There seems to have been a slight problem with the DBAsupport.com Forums database.
Please try again by pressing the refresh button in your browser.

An E-Mail has been dispatched to our Technical Staff, who you can also contact if the problem persists.

We apologise for any inconvenience.

Tuesday, June 14, 2005

Building a better dbms_stats

"Everybody Knows" that to get the Cost Based Optimizer (CBO) to choose a good execution plan you need accurate statistics. In the ideal world you would have up-to-date statistics at every moment. However, as tables shrink and grow their statistics no longer reflect the true condition of the table. The optimizer may choose a "wrong" path based on the information it has.

Prior to 8.1.5, calculating statistics used to be a trade off of time spent calculating statistics vs. the amount of time saved when your queries ran faster. Oracle provided the ANALYZE command or the dbms_utility package that you used to calculate statistics on your data. Automating the process of collecting statistics was either a script generating a bunch of ANALYZE commands or a call to dbms_utility.

The enhancements to the optimizer in 8.1.5 made calculating statistics a necessary evil. Oracle provided dbms_stats to assist with statistics gathering. My first attempt at using dbms_stats was replacing calls to dbms_utility with calls to dbms_stats. Needless to say, this method failed horribly wrong for a couple reasons:
  1. dbms_stats didn't work exactly the same as dbms_utility.
  2. dbms_stats calculation took longer than dbms_utility.
Once I learned what options for dbms_stats were good for my environment, I setup my analyze jobs and let them run. After a while, the weekly statistics calculation was taking 20 hours to run. Surely there must be a better way. My four requirements for a "better" dbms_stats were:
  • The new method must be able to analyze more than one object at a time. The degree option of dbms_stats is great, but I found my CPUs were at most 50% busy during statistics gathering, even with degree=2*CPU. I've got multiple processors and I want them 100% busy during statistics gathering.
  • The new method must be able to give me feedback on where it is and how long it took to analyze each object. I don't want to issue a command and wait for 5 hours to tell me "done".
  • 20 hours is totally unacceptable. I was analyzing every table every weekend when probably 90% of them never changed. I'll admit it, I was lazy when converting from another version.
  • When the optimizer chose a radically different execution path, I had to be able to "restore" the previous stats.

I read more about dbms_stats. I talked to people. I read what others were doing for their statistics collection. I read just about every link on AskTom about dbms_stats. I experimented.

First, I turned on monitoring and am letting Oracle tell me what objects I need to gather statistics for. This will immediately speed up my process because I don't have to analyze tables that don't change. Less work = faster.

Next, I configured job queues so I could use dbms_job. The job queues will give me the ability to run multiple analyze jobs at the same time.

Last, I had to write a little code. I basically broke up the processing into two pieces; one step to figure out what needs to be analyzed and submit those jobs to the job queue and another step to analyze the object. Below is the pseudo code I used to figure out which objects had to be analyzed:

BEGIN

Get A list of objects (o) that need analyzing;
FOREACH o LOOP
Record attributes about the object you are going to analyze;
Setup a DBMS_JOB;
DBMS_JOB.SUBMIT;
END LOOP;

END;

Then, each dbms_job was a simple call to a piece of code that used this logic:
BEGIN
Record the time this job started;

Export the stats for this object to a "history" table;
IF the table is partitioned THEN
IF the object to analyze is the partitioned table/index THEN
dbms_stats.gather_table/index_stats(
ownname=>o.ownname,
tabname=>o.tabname,
granularity=>'GLOBAL');
ELSE
dbms_stats.gather_table/index_stats(
ownname=>o.ownname,
tabname=>o.tabname,
partname =>o.partname);
END IF;
ELSE
dbms_stats.gather_table/index_stats(
ownname=>o.ownname,
tabname=>o.tabname);
END;
Record time job ended;
END;
I'm testing this in development right now to see how it compares to the old method. Right now it looks promising.

Friday, June 10, 2005

Where can I find help?

I'd like to consider myself as someone that contributes to the Oracle learning process for aspiring Database Administrators. Oracle is a big piece of software that we all need a little help in figuring out. The one thing you need to remember is someone has done what you are trying to do. When I get stuck on something, where do I go for help?

The first place I usually look is the online documentation at tahiti.oracle.com. There's usually a wealth of information on every little aspect of the piece of functionality you are searching for. The hardest part is filtering out all the extraneous things you don't need.

Next, I logon to Metalink and start searching for documents related to my problem. If you what you are looking for, Metalink is a great resource. My first seach is usually restricted to documents. If that doesn't yield enough results, I'll open it up to the forums as well.

If I still haven't found my answer, I'll navigate over to AskTom. The searching capability on this site is phenomenal. It has five years worth of questions and followup on just about every topic immaginable. If you get the rare chance to ask Tom a new question, definitely take it. You may even want to formulate your question beforehand and check back from time to time to see if he is accepting new ones.

Then I navigate to some other sites maintained by some people I really respect. You might not get a specific answer to your question, but may get some background on the subject you are researching. A great resource for install and "how-to" articles is OracleBase by Dr. Tim Hall. If you're dealing with latches or locking, definitely navigate to Steve Adams's IXOra. Howard J. Rogers site www.dizwell.com also has a lot of information and tips for DBAs and developers. Last, but not least check out Jonathan Lewis' site at http://www.jlcomp.demon.co.uk.

Then I'll start looking at "the forums". There are lots of question and answer forums for Oracle support and you have to pick and choose which ones you trust. I may be a little biased, but I prefer DBASupport.com and dizwell.com. Those forums are usually frequented by people that really want to help. As a hint, the people that answer questions really get turned off by subjects that contain "URGENT".

If all that fails solve the problem, I'll submit a TAR to OracleSupport. You won't get an immediate answer, and you might have to state your problem two or three times, but you'll eventually get either an answer or they can file a bug. If you're not happy with the progress of the TAR, you can always call the main support line and have the TAR "duty managed". Don't "duty manage" all your TARs, but only the ones that are important to business that might not be Sev 1's.

As a last resort, search the web. However, there's so much bad information out there that you really need to have an idea of what the solution is to help you in your search.

If anybody has any additional sites, please post them and share with the rest of us.

Thursday, June 09, 2005

NYOUG June Meeting

I got to the New York Oracle Users Group meeting about 30 minutes early today. As I was reviewing resumes' for my open position, I noticed out the corner of my eye Rachel Carmichael talking to someone about publishing books. I turned around to listen a little more closely and found Rachel talking to AskTom himself; Tom Kyte.

Rachel's keynote talk was titled "Life in the Trenches - Database Administation in the Real World" and was a survival guide to corporate politics. Some of her "pearls of wisdom":

Your job is do do what your boss tells you to do as long as it's not illegal or immoral.
Sure, you were hired as a DBA, but your boss asks you to write report XYZ. Do it and you get your boss' respect and he thinks your a team player.


There will always be somebody who knows more than you. There will always be somebody who knows less than you.

Life is a learning process. Help others learn what you know and others will help you learn.

Train your replacement.
This is actually not the first time I heard this. A former manager, Joe Murphy, always said his job as a manager was to work himself out of a job. His charges grow and he gets to move on to things more critical to the business.

As for Tom Kyte's keynote, lets just say he has a career as a comedian awaiting him when this Oracle gig is up. His presentation was very entertaining while exploring some of the "old school" thinking and how it relates to today's technology. 60 minutes of Tom Kyte and bind variables didn't come up once.

Monday, June 06, 2005

Gather Stale for Partitions #2

I found my answer to gathering statistics on partitioned tables. In short, the "LIST STALE"
option of dbms_stats.gather_database_stats will report the stale partition when
enough data has changed. In addition, if enough of the table structure has
changed, it will report that the entire table needs analyzing.


See example below:


Lets setup a partitioned table with 20 partitions and turn on monitoring for the table...


ops$jeffh@dev920.us> !more part_test_setup.sql

CREATE TABLE PartTest (
dt DATE,
txt VARCHAR2(60)
)
PARTITION BY RANGE (dt) (
PARTITION part_01 VALUES LESS THAN
(to_date('07/01/2005','mm/dd/yyyy')),
PARTITION part_02 VALUES LESS THAN
(to_date('08/01/2005','mm/dd/yyyy')),
PARTITION part_03 VALUES LESS THAN
(to_date('09/01/2005','mm/dd/yyyy')),
PARTITION part_04 VALUES LESS THAN
(to_date('10/01/2005','mm/dd/yyyy')),
PARTITION part_05 VALUES LESS THAN
(to_date('11/01/2005','mm/dd/yyyy')),
PARTITION part_06 VALUES LESS THAN
(to_date('12/01/2005','mm/dd/yyyy')),
PARTITION part_07 VALUES LESS THAN
(to_date('01/01/2006','mm/dd/yyyy')),
PARTITION part_08 VALUES LESS THAN
(to_date('02/01/2006','mm/dd/yyyy')),
PARTITION part_09 VALUES LESS THAN
(to_date('03/01/2006','mm/dd/yyyy')),
PARTITION part_10 VALUES LESS THAN
(to_date('04/01/2006','mm/dd/yyyy')),
PARTITION part_11 VALUES LESS THAN
(to_date('05/01/2006','mm/dd/yyyy')),
PARTITION part_12 VALUES LESS THAN
(to_date('06/01/2006','mm/dd/yyyy')),
PARTITION part_13 VALUES LESS THAN
(to_date('07/01/2006','mm/dd/yyyy')),
PARTITION part_14 VALUES LESS THAN
(to_date('08/01/2006','mm/dd/yyyy')),
PARTITION part_15 VALUES LESS THAN
(to_date('09/01/2006','mm/dd/yyyy')),
PARTITION part_16 VALUES LESS THAN
(to_date('10/01/2006','mm/dd/yyyy')),
PARTITION part_17 VALUES LESS THAN
(to_date('11/01/2006','mm/dd/yyyy')),
PARTITION part_18 VALUES LESS THAN
(to_date('12/01/2006','mm/dd/yyyy')),
PARTITION part_19 VALUES LESS THAN
(to_date('01/01/2007','mm/dd/yyyy')),
PARTITION part_20 VALUES LESS THAN
(to_date('02/01/2007','mm/dd/yyyy'))
)
/
ALTER TABLE PartTest MONITORING
/

ops$jeffh@dev920.us> @part_test_setup.sql

Table created.

Elapsed: 00:00:00.34

Table altered.

Elapsed: 00:00:00.02



Great. Now we need to fill that table with some data. We don't need random data, just a bunch of data in some of the partitions. I'll also analyze the table and see what modifications Oracle thinks is done.


ops$jeffh@dev920.us> !more part_test_fill.sql
DECLARE
myDate DATE;
BEGIN

FOR i IN 1..1000000 LOOP

myDate := SYSDATE+MOD(i,400);

INSERT INTO PartTest
VALUES (
myDate,
to_char(myDate,'YYYYMMDDHH24MISS')
);

IF mod(i,10000) = 0 THEN
COMMIT;
END IF;

END LOOP;

COMMIT;

END;
/
analyze table parttest compute statistics
/
set pages 2000

select partition_name, num_rows
from dba_tab_partitions
where table_name = 'PARTTEST'
order by partition_name
/

select
partition_name,
INSERTS,
UPDATES,
DELETES,
TIMESTAMP,
TRUNCATED,
DROP_SEGMENTS
from user_tab_modifications
where table_name = 'PARTTEST'
/

ops$jeffh@dev920.us> @part_test_fill

PL/SQL procedure successfully completed.

Elapsed: 00:06:02.39

Table analyzed.

Elapsed: 00:00:38.23

PARTITION_NAME NUM_ROWS
------------------------------ ----------
PART_01 62500
PART_02 77500
PART_03 77500
PART_04 75000
PART_05 77500
PART_06 75000
PART_07 77500
PART_08 77500
PART_09 70000
PART_10 77500
PART_11 75000
PART_12 77500
PART_13 75000
PART_14 25000
PART_15 0
PART_16 0
PART_17 0
PART_18 0
PART_19 0
PART_20 0

20 rows selected.

Elapsed: 00:00:00.40

no rows selected

Elapsed: 00:00:00.00
ops$jeffh@dev920.us>


Lets add some data to the later partitions just so Oracle will have something to report.


ops$jeffh@dev920.us> !more part_test_fill15.sql
DECLARE
myDate DATE;
BEGIN

FOR i IN 1..10000 LOOP

myDate := to_date('09/01/2006','mm/dd/yyyy')
- mod(i, 30);

INSERT INTO PartTest
VALUES (
myDate,
to_char(myDate,'YYYYMMDDHH24MISS')
);

END LOOP;

COMMIT;

END;
/
set pages 2000

select
partition_name,
INSERTS,
UPDATES,
DELETES,
TIMESTAMP,
TRUNCATED,
DROP_SEGMENTS
from user_tab_modifications
where table_name = 'PARTTEST'
/
ops$jeffh@dev920.us> @part_test_fill15

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.64

no rows selected

Elapsed: 00:00:00.01


Now, we have to wait 15 minutes until SMON picks up the changes.

OK. Lets see what dbms_stats.gather_database_stats tells us...


ops$jeffh@dev920.us> @need_to_analyze
OPS$JEFFH.PARTTEST(TABLE)|PART_15
OPS$JEFFH.PARTTEST(TABLE)|PART_16

PL/SQL procedure successfully completed.
Elapsed: 00:00:00.87


Alrighty, then. dbms_stats.gather_database_stats is telling us that PART_15 and PART_16 need to be analyzed.

Just for fun, lets see what happens when we drop a couple of the partitions in the "front" of the table...

ops$jeffh@dev920.us> alter table parttest 
drop partition part_01;

Table altered.

Elapsed: 00:00:00.54
ops$jeffh@dev920.us> alter table parttest
drop partition part_02;

Table altered.

Elapsed: 00:00:00.06
ops$jeffh@dev920.us> alter table parttest
drop partition part_03;

Table altered.

Elapsed: 00:00:00.07
ops$jeffh@dev920.us> alter table parttest
drop partition part_04;

Table altered.

Elapsed: 00:00:00.05
ops$jeffh@dev920.us> alter table parttest
drop partition part_05;

Table altered.

Elapsed: 00:00:00.05
ops$jeffh@dev920.us> @need_to_analyze
OPS$JEFFH.PARTTEST(TABLE)|PART_15
OPS$JEFFH.PARTTEST(TABLE)|PART_16
OPS$JEFFH.PARTTEST(TABLE)|

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.70
ops$jeffh@dev920.us>


Sweet. Oracle thinks we need to analyze PART_15, PART_16, and the entire PARTTEST table.

Sunday, June 05, 2005

Ever Notice?

Did you ever notice how four day weeks turn into 60 hour weeks? Monday (the holiday, of course) started out with a call about report X running 3 times longer than normal. After a couple of conversations with the developer found out that the execution plan had changed over the weekend. Tuesday and Wednesday had me figuring out why a query between two tables was doing 140+ LIOs per row and killing scalabiligy. Thursday I got to do a little research on statistics and partitioned tables. Friday, a developer comes to me and says the optimizer is choosing one plan when using the OCI JDBC Driver and another using the thin JDBC Driver. I say "No Way", we run some tests, and sure enough different plans. Maybe this week I'll get to finish my research on statistics. Arrr....

Wednesday, June 01, 2005

Gather Stale for Partitions

I'm modifying my statistics gathering process to take advantage of Oracle's ability to figure out what's stale and what's not. I know I can use dbms_stats.gather_schema_stats(options=>'GATHER STALE') to get statistics, but that just goes off into oblivion and I can't track it's progress. I decided to use the 'LIST STALE' and 'LIST EMPTY' options to get the tables that need statistics according to Oracle. I'm then going to record that list of tables and the amount of time spent analyzing each object.

One thing that I'm not sure of is with partitioned tables. Say I have a table with 20 partitions partitioned by date named part_01 through part_20. Each partition contains a month's worth of data. Months 1 through 17 contain about 120,000 records each. On the first day of the month, part_18 gets 20,000 rows and I COMPUTE statistics on the entire table. On the second day of the month, I add another 20,000 rows. I expect that dbms_stats.gather_schema_stats(options=>'LIST STALE') would list the partition as having stale statistics, but would it list the entire table as well?

I'm setting up a test....

Lessons the Hard Way #1

dbms_stats.alter_database_tab_monitoring(monitoring=>true) is not a setting. If you execute this method it just turns monitoring on for all your tables. New ones don't automatically get created with MONITORING turned on. Surely there's got to be a default setting...